选择所有子节点满足条件的自我关系中的行

时间:2012-08-22 22:37:55

标签: mysql sql join tree self-join

我正在使用包含以下字段的单个表格(名为documents):idparent_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可能是我的朋友,但我无法弄清楚如何使用它来获得预期的结果。

4 个答案:

答案 0 :(得分: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(CodeIgniter)中模拟了查询:

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

答案 1 :(得分:2)

如果我理解你正在寻找什么,我会使用以下方法来保持简单。

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

答案 2 :(得分:2)

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

答案 3 :(得分: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
                 )

这使用相关子查询来标识未满足条件的任何子文档。