2017-03-07 27 views
0

我有一個很難搞清楚如何做一個優化的查詢做下面列出,儘管這聽起來很簡單。查詢排除行的ID在另一個表

假設我有一個名爲promo的表(一列:ID),另一個表叫做promo_has_been_displayed_to_user(兩列:promo_id和user_id,而promo_id是引用promo.ID的外鍵)。我想要一個查詢,它將返回Promo中所有行,其中的ID字段在Promo_has_been_displayed_to_user中的任何行中都未提及,其中promo_has_been_displayed_to_user.user_id字段設置爲45.假設我在所有字段上都有索引。

(我的想法是,我有一個促銷廣告數據庫和一個用戶數據庫,每當我向用戶展示廣告時,我都會在promo_has_been_displayed_to_user中存儲它已經顯示給他們的內容。尚未顯示給用戶45)的新廣告

看來理論上最佳的方式做這將是如下:

1)獲取promo_has_been_displayed_to_user的一個子集,其中USER_ID = 45,並且在子集,維護user_id字段上的索引。 2)對於促銷中的每一行,取出ID並在索引的promo_id字段中查找步驟1中生成的子集。 3)返回宣傳片中的所有行,你沒發現在步驟2中

但如何匹配我的結構,反映了一個查詢?

現在,我至少有兩個查詢將返回正確的答案(我用測試數據驗證);問題是,我不認爲他們會最佳性能運行,有以下原因:

1)

select * from promo 
where ID not in (select promo_id from promo_has_been_displayed_to_user 
       where user_id=45) 

該查詢的麻煩是,一旦你必須返回ID列表「選擇從promo_has_been_displayed_to_user其中USER_ID = 45" PROMO_ID,我認爲這只是一個列表(沒有索引它身上),並認爲‘沒有’檢查是通過只檢查該列表一次一個實現。如果其中user_id = 45的promo_has_been_displayed_to_user的子集結果很大,那麼對於促銷中的每一行,我們都必須搜索一個沒有索引的巨大列表。

2)

select * from promo p 
where not exists (select * from promo_has_been_displayed_to_user 
        where promo_id = p.ID and user_id=45) 

這一次,我們正在做的索引PROMO_ID領域的查找。但是,對於促銷中的每一行,我正在查詢整個promo_has_been_displayed_to_user表。如果只有一小部分promo_has_been_displayed_to_user,其中user_id = 45,這是浪費的。

是否有一個單一的查詢將結合兩個世界的最佳 - 我首先將promo_has_been_displayed_to_user縮減到其中user_id = 45的子集,然後對於促銷中的每一行,我在promo_id上進行索引查找以查看是否子集中有匹配的行嗎?

(這是MySQL的5.0.95,雖然這聽起來像的東西是不是數據庫服務器特定的。)

+0

嘗試左連接。可能會在MySQL上執行得更快。 – jarlh

+0

這是一個簡單的排除加入 – Strawberry

回答

1

你不能用一個內部聯接做到這一點。你需要做的是一個反加入。通常,這是用這樣的查詢最容易實現:

SELECT * FROM A WHERE id NOT IN (SELECT id FROM B); 

這是SQL的antojoin的基本語法。

的另一種方法然而這樣做是把左連接到反連接和在某些數據庫這個性能更好:

SELECT A.* 
    FROM A 
    LEFT JOIN B ON A.id = B.id 
WHERE A.id IS NOT NULL AND B.id IS NULL; 

這原來是等效和一些數據庫可以更好地優化它。

+0

EXCEPT關鍵字也是一個選項。像這樣:'SELECT ID FROM EXCEPT SELECT ID FROM B'然而,你必須在兩個查詢中選擇相同的列類型。 – iPhantomGuy

+0

@iPhantomGuy,MySQL支持EXCEPT嗎?此外,OP選擇A中的所有列,而B可能不完全兼容。 – jarlh

+0

剛發現MySQL不支持EXCEPT。 – iPhantomGuy

相關問題