Mysql LEFT JOIN在25.000行以上变得非常慢

时间:2012-09-17 07:38:21

标签: mysql performance left-join

以下查询突然变得极其缓慢(似乎根本没有完成),表a中的行超过 25.000 行(尝试向查询添加限制x):

select a.*, b.column 
  from table_a a
left join table_b b 
  on (a.message_id = b.message_id) 
where date_sub(curdate(), interval 30 day) < a.date 
  and ( 
        b.message_id is NULL or 
        ( 
          b.message_id is not NULL 
          and a.message_id = b.message_id 
          and b.redeemed > 0 
        )
      )
order by a.id DESC

a.message_idb.message_id都有一个索引。 table_a将包含比table_b

更多的行

据我所知,左连接与组合为NULL 会导致mysql服务器达到极限,但我不知道如何重写查询以避免左连接。

基本上我想查询表a并检查表b中是否有匹配的条目。如果是这种情况,我想将表b中的日期合并到结果中。

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:1)

首先,我将最后一个条件简化为

AND ( b.message_id IS NULL OR b.redeemed > 0 )

现在您的查询具有以下基本结构:

a LEFT JOIN b
WHERE b IS NULL
   OR b.x > 0   /* MySQL may or may not be able to optimize LEFT JOIN + OR*/

我改写为UNION:

a LEFT JOIN b
WHERE b IS NULL

UNION ALL

a INNER JOIN b /* LEFT -> INNER */
WHERE b.x > 0

所以这是你的复杂查询:

(   SELECT
      a.*,
      NULL AS column  /* help MySQL a bit :) */
    FROM      table_a a
    LEFT JOIN table_b b ON a.message_id = b.message_id

    WHERE DATE_SUB(CURDATE(), INTERVAL 30 DAY) < a.date
      AND b.message_id IS NULL

) UNION ALL (

    SELECT
      a.*,
      b.column
    FROM       table_a a
    INNER JOIN table_b b ON a.message_id = b.message_id

    WHERE DATE_SUB(CURDATE(), INTERVAL 30 DAY) < a.date
      AND b.redeemed > 0
)

ORDER BY a.id DESC

您可以使用LEFT JOIN更改第一个子选择以使用NOT EXISTS,但我不知道它是否有帮助:

SELECT
  a.*,
  NULL AS column /* b's column(s) -> aliased NULL*/

FROM      table_a a

WHERE DATE_SUB(CURDATE(), INTERVAL 30 DAY) < a.date
  AND NOT EXISTS (
        SELECT 1
        FROM b
        WHERE a.message_id = b.message_id
      )