2

以下查询突然变得异常缓慢(似乎根本没有完成),表 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_id和都有一个索引b.message_id。table_a 的行数将比table_b

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

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

非常感谢任何帮助!

4

1 回答 1

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
      )
于 2012-09-17T08:11:06.473 回答