通过比较总的 -vs- 指定状态来检索 ID 列表
Retrieve list of ids by comparing total -vs- specified status
我有一份客户名单。每个客户可以有多个活动 (0..*)。每个 activity 都包含一个状态“is_completed”,它是一个布尔值 (True/False)。
我需要检索已完成所有活动的客户列表:
- 如果一个客户完成了所有的活动,我就会保留他。
- 如果客户没有完成所有活动,我将忽略他。
我写了一个 SQL 查询来完成这项工作,但我不确定它是否经过优化:
SELECT DISTINCT cc.client_id
FROM clients_clientactivity AS cc
LEFT JOIN clients_client AS c ON (c.id = cc.client_id)
WHERE c.client_type_id = 2
AND (
SELECT COUNT(cc1.id) FROM clients_clientactivity AS cc1 WHERE cc1.client_id = cc.client_id
) = (
SELECT COUNT(cc2.id) FROM clients_clientactivity AS cc2 WHERE cc2.is_completed = True AND cc2.client_id = cc.client_id
);
我该如何改进它?
感谢您的帮助。
您可以在 select 中使用 not 来表示不正确的
SELECT DISTINCT cc.client_id
FROM clients_clientactivity AS cc
LEFT JOIN clients_client AS c ON (c.id = cc.client_id)
WHERE c.client_type_id = 2
AND cc.client_id NOT IN (
SELECT cc2.client_id
FROM clients_clientactivity AS cc2
WHERE cc2.is_completed != True
)
我会使用聚合和 having
:
SELECT c.id
FROM clients_clientactivity ca JOIN
clients_client c
ON c.id = ca.client_id
WHERE c.client_type_id = 2
GROUP BY c.id
HAVING COUNT(*) = SUM(ca.iscompleted)
您的 WHERE
子句将 LEFT JOIN
转换为 INNER JOIN
,因此我删除了 LEFT JOIN
。
让我们进一步简化:
SELECT client_id
FROM clients_clientactivity
WHERE MIN(is_completed) = TRUE
GROUP BY client_id
(真==1,假==0)
子查询通常很慢。 NOT IN ( SELECT ... )
真的很糟糕(除非优化器神奇地变得更聪明了)。
你没有解释如何 client_type_id = 2
,但可能是这样的:
clients_client
SELECT a.client_id
FROM clients_client AS c
JOIN clients_clientactivity AS a ON (c.id = a.client_id)
WHERE MIN(a.is_completed) = TRUE
AND c.client_type_id = 2
GROUP BY a.client_id
如果性能有问题,那么:
c
需要 INDEX(client_type_id, id)
a
需要 INDEX(client_id, is_completed)
我有一份客户名单。每个客户可以有多个活动 (0..*)。每个 activity 都包含一个状态“is_completed”,它是一个布尔值 (True/False)。
我需要检索已完成所有活动的客户列表:
- 如果一个客户完成了所有的活动,我就会保留他。
- 如果客户没有完成所有活动,我将忽略他。
我写了一个 SQL 查询来完成这项工作,但我不确定它是否经过优化:
SELECT DISTINCT cc.client_id
FROM clients_clientactivity AS cc
LEFT JOIN clients_client AS c ON (c.id = cc.client_id)
WHERE c.client_type_id = 2
AND (
SELECT COUNT(cc1.id) FROM clients_clientactivity AS cc1 WHERE cc1.client_id = cc.client_id
) = (
SELECT COUNT(cc2.id) FROM clients_clientactivity AS cc2 WHERE cc2.is_completed = True AND cc2.client_id = cc.client_id
);
我该如何改进它?
感谢您的帮助。
您可以在 select 中使用 not 来表示不正确的
SELECT DISTINCT cc.client_id
FROM clients_clientactivity AS cc
LEFT JOIN clients_client AS c ON (c.id = cc.client_id)
WHERE c.client_type_id = 2
AND cc.client_id NOT IN (
SELECT cc2.client_id
FROM clients_clientactivity AS cc2
WHERE cc2.is_completed != True
)
我会使用聚合和 having
:
SELECT c.id
FROM clients_clientactivity ca JOIN
clients_client c
ON c.id = ca.client_id
WHERE c.client_type_id = 2
GROUP BY c.id
HAVING COUNT(*) = SUM(ca.iscompleted)
您的 WHERE
子句将 LEFT JOIN
转换为 INNER JOIN
,因此我删除了 LEFT JOIN
。
让我们进一步简化:
SELECT client_id
FROM clients_clientactivity
WHERE MIN(is_completed) = TRUE
GROUP BY client_id
(真==1,假==0)
子查询通常很慢。 NOT IN ( SELECT ... )
真的很糟糕(除非优化器神奇地变得更聪明了)。
你没有解释如何 client_type_id = 2
,但可能是这样的:
clients_client
SELECT a.client_id
FROM clients_client AS c
JOIN clients_clientactivity AS a ON (c.id = a.client_id)
WHERE MIN(a.is_completed) = TRUE
AND c.client_type_id = 2
GROUP BY a.client_id
如果性能有问题,那么:
c
需要 INDEX(client_type_id, id)
a
需要 INDEX(client_id, is_completed)