我从来没有在 MySQL 中使用 IN 获得过良好的性能,并且再次遇到了性能问题。
我正在尝试创建一个 View 。其相关部分是:
SELECT
c.customer_id,
....
IF (c.customer_id IN (
SELECT cn.customer_id FROM customer_notes cn
), 1, 0) AS has_notes
FROM customers c;
基本上,我只是想知道客户是否附有注释。有多少笔记并不重要。我如何使用 JOIN 重写它以加快速度?
客户表当前有 150 万行,因此性能是一个问题。
最佳答案
您不需要选择的客户 ID 吗?就目前情况而言,您是否不是为每个客户运行一次子查询,并获取一系列真值或假值,而不知道哪个值适用于哪个客户?
如果这就是您所需要的,则不需要引用客户表(除非您将数据库保持在语义不完整的状态,并且 customer_notes 中可能存在没有相应客户的条目 - 但随后您有比该查询的性能更大的问题);您可以简单地使用:
SELECT DISTINCT Customer_ID
FROM Customer_Notes
ORDER BY Customer_ID;
获取 Customer_Notes 表中至少包含一个条目的客户 ID 值列表。
如果您想要客户 ID 值的列表和关联的真/假值,那么您需要执行联接:
SELECT C.Customer_ID,
CASE WHEN N.Have_Notes IS NULL THEN 0 ELSE 1 END AS Has_Notes
FROM Customers AS C
LEFT JOIN (SELECT Customer_ID, COUNT(*) AS Have_Notes
FROM Customer_Notes
GROUP BY Customer_ID) AS N
ON C.Customer_ID = N.Customer_ID
ORDER BY C.Customer_ID;
如果这导致性能不佳,请检查您是否在 Customer_Notes.Customer_ID 上有索引。如果这不是问题,请研究查询计划。
<小时/>Can't do ... in a view
在任何 DBMS 中,对 View 中允许的内容的微小限制总是令人讨厌的(MySQL 并不是唯一有限制的)。但是,我们可以通过单个常规连接来完成此操作。我刚想起来。 COUNT(column)
仅计算非空值,如果所有值都为空,则返回 0,因此 - 如果您不介意获得计数而不仅仅是 0 或 1 - 您可以使用:
SELECT C.Customer_ID,
COUNT(N.Customer_ID) AS Num_Notes
FROM Customers AS C
LEFT JOIN Customer_Notes AS N
ON C.Customer_ID = N.Customer_ID
GROUP BY C.Customer_ID
ORDER BY C.Customer_ID;
如果你绝对必须有 0 或 1:
SELECT C.Customer_ID,
CASE WHEN COUNT(N.Customer_ID) = 0 THEN 0 ELSE 1 END AS Has_Notes
FROM Customers AS C
LEFT JOIN Customer_Notes AS N
ON C.Customer_ID = N.Customer_ID
GROUP BY C.Customer_ID
ORDER BY C.Customer_ID;
请注意,“N.Customer_ID”的使用至关重要 - 尽管表中的任何列都可以(但您没有透露任何其他列的名称,AFAICR),并且我通常会使用除为了清晰起见,加入专栏。
关于mysql - 将 IN 子查询重写为 JOIN,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4466336/