1

我有一个表来跟踪每个用户单击的链接,我还有另一个表带有链接。这是每个表结构:

链接: id | 链接 | 价值 | 添加日期

点击: user_id | 链接ID | date_clicked

现在这是我用来进行搜索的代码并且它有效,我只想知道是否有更有效的方法来做这件事,因为点击的链接表会很快变得非常大。

$history_query = mysql_query("SELECT * FROM clicked_links WHERE user_id = '$id'") or die(mysql_error());
$history_array = array();
while ($h = mysql_fetch_array($history_query)) {
    $history_array[] = $h['link_id'];
}
$clicked = implode(',', $history_array);

$link_query = mysql_query("SELECT * FROM chip_links WHERE id NOT IN ($clicked) ORDER BY value DESC") or die(mysql_error());
while ($r = mysql_fetch_array($link_query)) {
    echo "<div id='claim{$r['id']}' style='text-align: center; font-weight: bold; font-size: 18px; float: left; width: 183px;'>
    <a href='{$r['link']}' id='{$r['id']}' class='collect' target='_blank'>
    Claim {$r['value']} points!
    </a>
    </div>";
}
4

2 回答 2

1

运行单个查询来获取结果集比运行单独的查询更有效。

您不需要返回所有link_id值,将它们放入数组中,将数组放入字符串中,然后将该字符串推送到另一个查询中,然后将其随机播放回数据库……数据库已经有了。

此查询将返回与您当前的 $link_query 等效的结果集,而不需要 $history_query 或 $history_array。

SELECT l.id
     , l.link
     , l.value
  FROM chip_links l
 WHERE l.id NOT IN
       ( SELECT c.link_id
           FROM clicked_links c
          WHERE c.user_id = '$id'
            AND c.link_id IS NOT NULL
       )
 ORDER BY l.value DESC

如果您无法保证 clicked_links 表中的 link_id 不为 NULL,则您需要link_id IS NOT NULL在该子查询中包含谓词,因为如果 link_id 值为 NULL,则查询不会返回任何行。(这是一个众所周知且可避免的NOT IN (subquery)构造问题。

MySQL 很可能会将其优化为(希望更有效但)等效的 NOT EXISTS 相关子查询,如下所示:

SELECT l.id
     , l.link
     , l.value
  FROM chip_links l
 WHERE NOT EXISTS 
       ( SELECT 1 
           FROM clicked_links c
          WHERE c.user_id = '$id'
           AND c.link_id = l.id
       )
 ORDER BY l.value DESC

但是,为了获得最佳性能,您可能希望使用反连接模式。

LEFT JOIN 操作基本上是寻找匹配的行,IS NOT NULL谓词会抛出匹配的行,所以你得到的chip_links是没有“匹配”行的行clicked_links

MySQL 优化器通常使用如下查询生成最有效的计划:

SELECT l.id
     , l.link
     , l.value
  FROM chip_links l
  LEFT
  JOIN clicked_links c
    ON c.link_id = l.id
   AND c.user_id = '$id'
 WHERE c.link_id IS NULL
 ORDER
    BY l.value DESC

为了在大型集合上获得良好的性能,您可能还需要索引

... ON clicked_links (user_id, link_id)

... ON chip_links (value, id, link)

这应该允许完全从索引中满足查询,而不需要排序操作。EXPLAIN 输出将包括“使用索引”,不包括“使用文件排序”)。

于 2013-01-15T23:47:28.247 回答
0

像这样的一次性查询会告诉你给定用户没有点击的所有链接

SELECT l.* FROM chip_links l
LEFT JOIN clicked_links c ON (c.link_id=l.id AND l.user_id='$id')
WHERE c.link_id IS NULL
ORDER BY l.value DESC;

如果您不熟悉左连接,它将包含来自 clicked_links 中连接子句匹配的行,但如果我们没有匹配,我们只会得到空值。由于我们对不匹配感兴趣,WHERE 子句确保这些是我们将获得的唯一行。

这可能比使用两个查询和一些 PHP 代码更有效,但只有一个基准可以肯定地告诉你。您还应该检查 的输出EXPLAIN SELECT ...以确保使用了合适的索引。

于 2013-01-15T23:43:47.747 回答