2013-01-15 36 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 ...的輸出以確保正在使用合適的索引。

相關問題