2011-07-19 107 views
8

我有一個MySQL數據庫,其中表A與表B有一對多關係,並且我希望選擇表B中沒有子表的所有行答:我已經使用只有在沒有子女的情況下選擇父行

SELECT id FROM A WHERE NOT EXISTS (SELECT * FROM B WHERE B.id=A.id) 

SELECT id FROM A LEFT JOIN B ON A.id=B.id WHERE B.id IS NULL 

這兩個似乎慢嘗試。有更快的查詢來實現相同的事情嗎?

如果這是相關的,在我的數據庫表中A有大約500,000行,而表B有大約3到4百萬行。

編輯:對於我的數據庫中的實際表,解釋給我:

+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+ 
| id | select_type  | table   | type | possible_keys | key      | key_len | ref | rows | Extra     | 
+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+ 
| 1 | PRIMARY   | frontend_form471 | index | NULL   | frontend_form471_61a633e8 | 32  | NULL | 671927 | Using where; Using index | 
| 2 | DEPENDENT SUBQUERY | SchoolData  | index | PRIMARY  | PRIMARY     | 49  | NULL | 3121110 | Using where; Using index | 
+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+ 

select number from frontend_form471 where not exists (select * from SchoolData where SchoolData.`f471 Application Number`=frontend_form471.number) 

+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+ 
| id | select_type | table   | type | possible_keys | key      | key_len | ref | rows | Extra           | 
+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+ 
| 1 | SIMPLE  | frontend_form471 | index | NULL   | frontend_form471_61a633e8 | 32  | NULL | 671927 | Using index; Using temporary     | 
| 1 | SIMPLE  | SchoolData  | index | PRIMARY  | PRIMARY     | 49  | NULL | 3121110 | Using where; Using index; Not exists; Distinct | 
+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+ 

select distinct number from frontend_form471 left join SchoolData on frontend_form471.number=SchoolData.`f471 Application Number` where SchoolData.`f471 Application Number` is NULL 

其中在我的情況frontend_form471是表A和SchoolData是表B中

EDIT2:在表B(SchoolData)在我的數據庫,ID是一個兩部分的主鍵的第一部分,所以它是編入索引並且B中仍有多個條目具有相同的ID。

+0

'解析SELECT ID從左邊加入B.在A.id = B.id WHERE B.id IS NULL'你能發佈兩個查詢的EXPLAIN結果嗎? – Igor

+0

索引沒有幫助嗎? – Londeren

+0

是否選擇「COUNT(*)= 0」更快? –

回答

8
SELECT id FROM A LEFT OUTER JOIN B ON A.id=B.id WHERE B.id IS NULL 

你可以這樣做。外連接應該帶來一點表現,但不是很多。

無論如何,新的數據庫系統可能會優化您的查詢,所以不會有任何區別。

這裏正確的方法是緩存!如果可能,請嘗試查詢cacher和應用程序級別緩存。

當然你需要適當的索引。

,並通過適當的我的意思是這兩個表,最好,因爲這將有靜態查找時間對比於具有對數中所有樹上的一個哈希索引

嘗試把看到什麼真正減緩下來查詢之前的解釋。

如果你真的需要這樣做的話,你可能會重構你的數據結構。

您可能會創建一個觸發器來標記表A中的標誌是否在表be中有相應的條目。當然這個id數據冗餘,但有時它的價值。只是將其視爲緩存。最後

一個念頭:你可以嘗試SELECT id FROM A WHERE id NOT IN (SELECT id FROM B)它可能會快一點,因爲沒有實際的結合是必要的,但它也可能會比較慢,因爲在集合中的查詢將是一個全面掃描。我不太確定這將如何處理,但它可能值得一試。

+0

這是最好的解決方案...它可以匹配或不匹配,但只有當它不存在時纔會返回記錄...通過父表的單個循環...類似於過去我也提供的方法。 – DRapp

+2

只有MySQL有這個:其他引擎更好,不存在http://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/ – gbn

+0

我認爲你最重要的一點是關於散列指數。如果可以的話,我會使用它們,但是InnoDB不支持它們,而且我不準備切換引擎來使這個查詢正常工作。 – murgatroid99

1

不管你怎麼看它都會變得很慢。最差的表現將是一個全面的交叉連接,創造2萬億潛在的匹配(4 mill * 500k)。

第二個很可能會執行得更快,因爲它是一個單一的查詢。

1

你可以嘗試

SELECT id FROM A WHERE A.id NOT IN (SELECT id FROM B) 

,但我不知道這是否會是任何更快。我會先嚐試左連接。我認爲你的問題更多的是與索引有關。你有兩個ID字段的索引。

0

請務必在A.idB.id上有一個索引。

看起來有點奇怪的是,你加入A.id與B.id. B.是A的外鍵還是B的主鍵?

+0

B.id是A的外鍵和兩列主鍵的一半。 – murgatroid99

+0

重要嗎?當然maby的數據結構可能會變得很糟糕。 –

+0

只是想確保連接是好的。 – phlogratos

1

您的索引很差。

對於所有形式(EXISTS,IN,LEFT JOIN),你應該有ID指標在

+0

id-s看起來像PK,所以查詢應該很快。 – Igor

+0

@Igor:或者子表具有它自己的代理(這裏沒有使用,id是FK列),或者id是組合鍵的一部分。除非它是1:1的關係......所以你不能假設兩邊都有正確的索引 – gbn

+0

B.id絕對不是PK,因爲在B中有許多行具有相同的ID。 – phlogratos

0

如果你的模式是這樣的:

CREATE TABLE b(
    id int, 
    value varchar(255) 
) 

CREATE TABLE a(
    id int, 
    father_id int, 
    value varchar(255) 
) 

如果你希望所有的在表A中沒有孩子的行A爲什麼你不嘗試類似:

SELECT * FROM B WHERE id NOT IN (SELECT father_id FROM A GROUP BY father_id) 

我還沒有測試,但我認爲它的潰爛。記得把指數在ID

希望這有助於

0

爲什麼不嘗試空值,而不是NULL。在SQL中,與任何其他值(即使爲NULL)相比,NULL值都不會爲真。除非表達式中涉及的運算符和函數的文檔中另有說明,否則包含NULL的表達式始終會生成NULL值。

相關問題