MySQL Distinct 性能不佳

标签 mysql sql database performance distinct

我有一个包含多个左连接的 Distinct Select 语句,当我的 where 子句很大时,它的性能很差。以下是我的声明

SELECT  DISTINCT u.*, ri.id as reg_id, d.id as dist_id
    FROM  users u
    LEFT JOIN  earned_points ep ON u.id = ep.user_id
    LEFT JOIN  distributors d ON d.id = ep.distributor_id
      OR  d.id = u.distributor_id
      OR  d.id = u.additional_distributor_id
    LEFT JOIN  registration_items_users riu ON u.id = riu.user_id
      AND  riu.distributor_id = d.id
      AND  riu.registration_item_id = 21
    LEFT JOIN  registration_items ri ON riu.registration_item_id = ri.id
    WHERE  d.id IN (201,281,321,631,901,971,1211,1601,1611,1621,
               1631,1641,1651,1661,1671,1681,1691,1701,1711,1721,1731,
               1741,1751,1761,1771,1781,2281,2291,2401,2781,2801,2931 );

此查询的解释如下: select_explain

此查询大约需要 4 秒才能完成。如果我将 where 减少到一个 id,那么它会加速到大约 170 毫秒。

如有任何关于如何加快查询速度的建议,我们将不胜感激。

谢谢

编辑

我能够根据 Rick James(接受的答案)的建议提出解决方案。使用 Union 并摆脱 Left Joins 和 Distinct 就可以了。与上面的 4 秒版本相比,这个新查询大约需要 200 毫秒。

(SELECT  u.*, 
   (SELECT riu.registration_item_id 
       FROM registration_items_users riu 
       WHERE riu.user_id = u.id 
           AND riu.distributor_id = d.id 
           AND riu.registration_item_id = 21) as reg_id,
   d.id as dist_id
   FROM users u
   JOIN earned_points ep ON u.id = ep.user_id
   JOIN distributors d ON d.id = ep.distributor_id
       WHERE d.id IN (201,281,321,631,901,971,1211,1601,1611,1621,
            1631,1641,1651,1661,1671,1681,1691,1701,1711,1721,1731,
            1741,1751,1761,1771,1781,2281,2291,2401,2781,2801,2931))
   UNION
(SELECT  u.*, 
   (SELECT riu.registration_item_id 
       FROM registration_items_users riu 
       WHERE riu.user_id = u.id 
           AND riu.distributor_id = d.id 
           AND riu.registration_item_id = 21) as reg_id,
   d.id as dist_id
   FROM users u
   JOIN distributors d ON d.id = u.distributor_id
       WHERE d.id IN (201,281,321,631,901,971,1211,1601,1611,1621,
            1631,1641,1651,1661,1671,1681,1691,1701,1711,1721,1731,
            1741,1751,1761,1771,1781,2281,2291,2401,2781,2801,2931))
   UNION
(SELECT  u.*, 
   (SELECT riu.registration_item_id 
       FROM registration_items_users riu 
       WHERE riu.user_id = u.id 
           AND riu.distributor_id = d.id 
           AND riu.registration_item_id = 21) as reg_id,
   d.id as dist_id
   FROM users u
   JOIN distributors d ON d.id = u.additional_distributor_id
       WHERE d.id IN (201,281,321,631,901,971,1211,1601,1611,1621,
            1631,1641,1651,1661,1671,1681,1691,1701,1711,1721,1731,
            1741,1751,1761,1771,1781,2281,2291,2401,2781,2801,2931))

最佳答案

EXPLAIN 中,查看 u 行。它正在执行大约 6974 行的“表扫描”。

去掉 LEFT 除非“右”表是可选的。

OR转换为UNION;那就是索引让您失望的地方。 (UNION ALLUNION DISTINCT 更快;选择一个有意义的。)

假设 LEFTs 可以被删除,并且 DISTINCT 可以从 SELECT 移动到 UNION:

SELECT  u.*, ri.id as reg_id, d.id as dist_id
    FROM  users u
    JOIN  earned_points ep ON u.id = ep.user_id  -- ep needed only for this
    JOIN  distributors d ON d.id = ep.distributor_id  -- This one line differs
    JOIN  registration_items_users riu ON u.id = riu.user_id
      AND  riu.distributor_id = d.id
      AND  riu.registration_item_id = 21
    JOIN  registration_items ri ON riu.registration_item_id = ri.id
    WHERE  d.id IN (201,281,321,631,901,971,1211,1601,1611,1621,
                1631,1641,1651,1661,1671,1681,1691,1701,1711,1721,1731,
                1741,1751,1761,1771,1781,2281,2291,2401,2781,2801,2931 
                   )
    UNION  DISTINCT 
SELECT  u.*, ri.id as reg_id, d.id as dist_id
    FROM  users u
    JOIN  distributors d ON d.id = u.distributor_id
    JOIN  registration_items_users riu ON u.id = riu.user_id
      AND  riu.distributor_id = d.id
      AND  riu.registration_item_id = 21
    JOIN  registration_items ri ON riu.registration_item_id = ri.id
    WHERE  d.id IN (201,281,321,631,901,971,1211,1601,1611,1621,
                1631,1641,1651,1661,1671,1681,1691,1701,1711,1721,1731,
                1741,1751,1761,1771,1781,2281,2291,2401,2781,2801,2931 
                   )
    UNION  DISTINCT 
SELECT  u.*, ri.id as reg_id, d.id as dist_id
    FROM  users u
    JOIN  distributors d ON d.id = u.additional_distributor_id
    JOIN  registration_items_users riu ON u.id = riu.user_id
      AND  riu.distributor_id = d.id
      AND  riu.registration_item_id = 21
    JOIN  registration_items ri ON riu.registration_item_id = ri.id
    WHERE  d.id IN (201,281,321,631,901,971,1211,1601,1611,1621,
                1631,1641,1651,1661,1671,1681,1691,1701,1711,1721,1731,
                1741,1751,1761,1771,1781,2281,2291,2401,2781,2801,2931 
                   ) ;

跨列展开数组通常不是一个好主意。这似乎是 distributors 正在发生的事情。而这个烂摊子可能就是这样的结果。

编辑

更好的方法是将 rirui 内容从选择中拉出并将其转换为子查询。这是要点;我没有精力把它全部写完:

SELECT x.*,
        ( SELECT ... ri and rui stuff ... ) AS reg_id
    FROM (
        --  from above, less the ri and rui stuff:
        SELECT ...
        UNION DISTINCT
        SELECT ...
        UNION DISTINCT
        SELECT ...
         ) AS x;

关于MySQL Distinct 性能不佳,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35560850/

相关文章:

mysql - 将 MySQL 数据库置于版本控制之下?

mysql在一列中加入多个值

sql - PHP/MySQL COUNT 似乎没有正常工作

sql - 为什么并行运行简单查询会使其更快?

mysql - "Invalid Column Name"旧数据库值错误

sql-server - 如何在数据库中搜索带有特殊字符(')的记录

Java - 在异常后请求的时间内无法获得锁

mysql - 如何保证MySQL复制准确?

mysql - 根据用户 ID 显示 JSON 内容 iOS

Mysql - 获取重复结果