通过比较总的 -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)