mysql - 将 IN 子查询重写为 JOIN

标签 mysql join sql-optimization in-subquery

我从来没有在 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/

相关文章:

sql - 多次调用带有数组参数的集合返回函数

php - sql 查询需要时间,因为我正在查看三个表

php - 格式化 MySQL 查询结果

mysql - 如何使用mysql用户定义函数生成高斯分布

MySQL 结合外连接

mysql - 提供额外的 bool 类型返回列,用于评估另一个查询

MySQL:有什么办法可以将这N个查询变成更少的查询吗?

sql - Postgres 中的慢速查询优化

mysql - bash: 从 mysql 查询中输出\n\t 个字符

php - PDO INSERT不在mysql中注册重复记录