2013-01-15 34 views
1

我有一个表跟踪每个用户点击的链接,并且我有另一个包含链接的表。这里是每个表结构:从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>"; 
} 
+0

尝试使用'NOT EXISTS' – Kermit

+0

[**请勿在新代码**中使用'mysql_ *'函数](http://bit.ly/phpmsql)。他们不再被维护[并被正式弃用](https://wiki.php.net/rfc/mysql_deprecation)。看到[**红框**](http://j.mp/Te9zIL)?学习[*准备的语句*](http://j.mp/T9hLWi),并使用[PDO](http://php.net/pdo)或[MySQLi](http://php.net/ mysqli) - [这篇文章](http://j.mp/QEx8IB)将帮助你决定哪个。如果你选择PDO,[这里是一个很好的教程](http://j.mp/PoWehJ)。 –

+0

谢谢你的教程链接。我一直想要改变方向,但我的办公室里已经有了一个完整的盘子,很少有时间学习。这实际上是我在学习新功能之前用旧功能进行抽取的最后一个网站,因为它是一个仅在2周内活动的微型网站。 –

回答

1

运行单个查询来获取结果集,而不是运行单独的查询会更高效。

您不需要返回所有值,将它们放入数组中,将数组放入字符串中,然后将该字符串推入另一个查询中,然后将其拖回到数据库中...数据库已经存在有这个。

该查询将返回与当前$ 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 IS NOT 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没有“匹配”行。

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”)。

+0

谢谢!这对我来说非常合适,我在这个过程中学到了一些新东西。我从来没有太多的MySQL人,但它开始在我身上成长。 :) –

+0

但是我确实有一个问题。当你声明'SELECT l。* FROM chip_links l'时,'l'在这个语句中代表什么? –

+0

在这种情况下,'l'是一个表别名。通过将该别名分配给该表,我可以限定该表中具有该别名的列的所有引用。 'l.value'表示'chip_links'表中的'value'列。表别名的使用有几个优点:使更复杂的语句更易于阅读和理解,当限定条件时,关键字的列名不必包含在反引号内,限定列可避免查询抛出“模糊列”异常(特别是在新列被添加到表中);他们是必需的派生表等。 – spencer7593

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; 

所有链接。如果你不熟悉LEFT JOIN,它将包括来自行clicked_links在连接子句匹配的地方,但是我们没有找到匹配的地方,我们只会得到空值。由于我们对非匹配感兴趣,因此WHERE子句确保这些是我们将获得的唯一行。

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

相关问题