将子查询重写为JOIN

时间:2010-12-16 23:22:57

标签: mysql join sql-optimization in-subquery

我在MySQL中从未表现出良好的性能,而且我再次遇到了性能问题。

我正在尝试创建一个视图。相关部分是:

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重写它以加快速度?

customers表目前有150万行,因此性能问题。

3 个答案:

答案 0 :(得分:1)

我认为EXISTSJOININ更适合您的情况。

SELECT 
   IF (EXISTS ( 
        SELECT *
        FROM customer_notes cn 
        WHERE c.customer_id = cn.customer_id),
       1, 0) AS filter_notes 
FROM customers 

答案 1 :(得分:1)

您是否需要选择客户ID?按照目前的情况,您是不是每个客户都运行一次子查询,并获得一个真值或假值的流,而不知道哪个适用于哪个客户?

如果这是您所需要的,您不需要引用customers表(除非您将数据库保持在语义不完整的状态,并且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上有索引。如果这不是问题,请研究查询计划。


  

在视图中无法做到

视图中允许的内容的小限制在任何DBMS中总是令人讨厌(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),我通常使用的不是加入专栏以获得清晰度。

答案 2 :(得分:0)

试试这个

SELECT
  CASE WHEN cn.customer_id IS NOT NULL THEN 1
        ELSE 0
    END     AS filter_notes
  FROM customers c LEFT JOIN customer_notes cn
    ON c.customer_id= cn.customer_id