2010-06-01 24 views
3

我在MySQL中看到一個很奇怪的行爲,看起來像一些奇怪的錯誤。我知道通常會將經過驗證的工具歸咎於自己的錯誤,但我一直在討論這個問題。奇怪的MySQL行爲,看起來像一個SQL錯誤

我有2個表,我,與2797條記錄,和C,與1429 C參考一
我想要刪除未使用的C在我所有的記錄,所以我做:

select * from i where id not in (select id_i from c); 

返回0條記錄,在給定每個表中的記錄計數的情況下,它在物理上是不可能的。我也非常確定查詢是正確的,因爲它與前兩個小時使用的相同類型的查詢來清理其他帶有孤立記錄的表。

爲了使事情更離奇......

select * from i where id in (select id_i from c); 

不工作,並給我帶來了1297條記錄,我不想刪除。
因此,IN工作,但不是不。

更糟糕的是:

select * from i where id not in (
    select i.id from i inner join c ON i.id = c.id_i 
); 

,做的工作,但它應該是等同於第一個查詢(我只是在這一點上想瘋了的東西)。
唉,我不能使用這個查詢來刪除,因爲我正在使用我從子查詢中刪除的同一個表。

我假設東西在我的數據庫已損壞在這一點上。

萬一它很重要,這些都是沒有任何外鍵的MyISAM表格,而且我在我的開發機器和生產服務器上運行了相同的查詢,結果相同,因此無論那裏有什麼腐敗存在一個mysqldump /源代碼循環,聽起來很奇怪。

關於可能出錯的任何想法,或者更重要的是,我該如何修復/解決此問題?

謝謝!
Daniel

回答

9

我猜你有一行包含NULL值。 INNOT IN的行爲方式在存在NULL值時您可能不會期望。從manual

爲符合SQL標準,在返回NULL不僅如果在左側表達式爲NULL,而且如果不匹配,在列表中找到,並在其中的一個表達式列表是NULL。

NOT NULL的值仍然是NULL,不是你想要的。

這裏的行爲對一個簡化的例子演示:

CREATE TABLE c (id_i INT NULL); 
INSERT INTO c (id_i) VALUES 
(1), 
(2), 
(NULL); 

CREATE TABLE i (id INT NOT NULL); 
INSERT INTO i (id) VALUES 
(1), 
(2), 
(3); 

SELECT * FROM i WHERE id NOT IN (SELECT id_i FROM c); -- returns nothing 
SELECT * FROM i WHERE id IN (SELECT id_i FROM c);  -- returns 1,2 
SELECT * FROM i WHERE id NOT IN (     -- returns 3 
    SELECT i.id FROM i INNER JOIN c ON i.id = c.id_i 
); 
+0

哇,我感到令人難以置信的愚蠢,非常感謝你,非常,非常驚訝,我從來沒有發現這種行爲在超過10年的使用SQL。 非常感謝您的先生! – 2010-06-01 21:32:06

+2

@Daniel - 'NOT IN(...,NULL)'總是不會返回任何內容。要了解爲什麼會出現這種情況,請參閱此鏈接http://www.simple-talk.com/sql/learn-sql-server/sql-and-the-snare-of-three-valued-logic/ – 2010-06-01 21:43:43

2

我想刪除未使用的C

對於它的價值時,MySQL在我的所有記錄支持對多表DELETE語法的標準SQL的擴展,您可以使用它來避開NOT IN謂詞和NULL和子查詢中的特質。

DELETE I 
FROM I LEFT OUTER JOIN C ON (i.id = c.id_i) 
WHERE c.id_i IS NULL; 

注:我沒有測試這個,它只是從內存中。請自己閱讀manual,並在一些示例數據上進行測試,以確保它在您的真實數據庫上使用之前能像預期的那樣工作。

1

如果您嘗試此查詢,您會得到什麼回報?

select * 
    from i 
where NOT EXISTS (select id_i 
        from c 
        where c.id_i = i.id 
       )