我正在使用包含以下字段的单个表格(名为documents
):id
,parent_id
和status
。 parent_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
可能是我的朋友,但我无法弄清楚如何使用它来获得预期的结果。
答案 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
)
这使用相关子查询来标识未满足条件的任何子文档。