2012-08-22 34 views
0

我正在使用一個表(稱爲documents),其中包含以下字段:id,parent_idstatusparent_id字段指的是同一個表中的id字段。 status字段的類型爲ENUM('submitted', 'accepted', 'rejected')選擇所有孩子都符合條件的自我關係中的行

我想選擇所有documents沒有孩子在哪裏status = 'accepted'

我第一次嘗試是這樣的:

SELECT DISTINCT `documents`.* 
FROM (`documents`) 
LEFT OUTER JOIN `documents` children_documents 
    ON `documents`.`id` = `children_documents`.`parent_id` 
WHERE `children_documents`.`id` IS NULL 
    OR `children_documents`.`status` != 'accepted' 

這樣做的問題是,既接受和孩子們接受的文件仍然會選擇。沒有任何文件接受的孩子應該被選中。

我有一種感覺GROUP BY可能是我的朋友,但我無法弄清楚我將如何使用它來獲得預期的結果。

回答

1
SELECT DISTINCT `documents`.* 
FROM (`documents`) 
LEFT OUTER JOIN `documents` children_documents 
    ON `documents`.`id` = `children_documents`.`parent_id` 
    AND `children_documents`.`status` = 'accepted' 
WHERE `children_documents`.`parent_id` IS NULL 
+0

如果我理解正確,最後一行也可以檢查''children_documents'。'id''。你是一個更優雅的解決方案,所以我將其標記爲已接受。不幸的是,如果我的ORM庫不支持多個「ON」條件,我最終可能不會使用它。 –

+0

是的,在左外連接中,當表中沒有匹配的表時,表中的所有列都將爲NULL,因此您可以檢查它們中的任何一個。作爲一種個人風格,我喜歡使用連接條件中提到的那種。否則,你必須留意可能包含顯式NULL的列。 – Barmar

2

我用MySQL CASE statement解決了這個問題。

SELECT DISTINCT `documents`.* 
FROM (`documents`) 
LEFT OUTER JOIN `documents` children_documents 
    ON `documents`.`id` = `children_documents`.`parent_id` 
GROUP BY `documents`.`id` 
HAVING SUM(CASE `children_documents`.`status` WHEN 'accepted' THEN 1 ELSE 0 END) = 0 

這選擇所有文件,不管他們是否有孩子,並計算他們接受的孩子的數量。該數字對於要選擇的行必須爲零。

編輯:對於好奇,我設法仿效DataMapper的ORM(笨)查詢:

$d->distinct()->where('status', 'accepted')->group_by('id') 
    ->having_func('!SUM', array('[CASE]', '@children/status', '[WHEN]', 'accepted', '[THEN]', 1, '[ELSE]', 0, '[END]'), NULL); 
+0

如果你解決了這個問題,你應該將其標記爲接受的答案。 – Taryn

+0

@bluefeet:StackOverflow不會讓你自我接受,直到兩天過去。 –

1

在MySQL中,最快的方式可能是這樣的:

select d.* 
from documents d 
where not exists (select 1 
        from documents c 
        where c.parent_id = d.id and 
         coalesce(c.status, '') = 'Accepted' 
        limit 1 
       ) 

這使用相關的子查詢來識別任何失敗條件的子文檔。

+0

子查詢實際上比聯接更快嗎? –

+0

@HenryMerriam。 。 。這種變化應該是。實際上從表中返回數據沒有任何開銷。另外,當有多場比賽時,應該將結果短路並在第一場比賽中停止。 –

+0

仔細一看,我認爲狀態檢查應該讀取'... AND c.status =='accepted''。 (我試圖選擇沒有被接受的孩子的文件。)這是一個有趣的方法。 –

2

如果我正確理解你在找什麼,我會用下面的方法來保持簡單。

SELECT * 
FROM `documents` 
WHERE `id` NOT IN ( 
    SELECT `parent_id` 
    FROM `documents` children_documents 
    WHERE `status` = 'accepted'); 
相關問題