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 に答える