从PHP和MySQL中的另一个表中排除项目

时间:2013-01-15 23:33:39

标签: php mysql

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

链接: id |链接|价值| DATE_ADDED

已点击: user_id | link_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>";
}

2 个答案:

答案 0 :(得分: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输出将包含“使用索引”,并且不包括“使用filesort”。

答案 1 :(得分: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的行,其中join子句匹配,但是我们没有得到匹配,我们只会得到空值。由于我们对不匹配感兴趣,因此WHERE子句确保这些是我们将获得的唯一行。

使用两个查询和一些PHP代码可能会更有效,但只有基准测试才能确定。您还应检查EXPLAIN SELECT ...的输出,以确保使用合适的索引。