2

かなり大きなデータセットで実行されるこのクエリがあります。
それは非常に遅いです...

このクエリを最適化する必要がありますが、どこから始めればよいかわかりません (インデックスは別として)。

前もって感謝します!

SELECT d.distributor_id, 
d.first_name,
d.last_name,
d.sponsor_id,
COUNT(f.business_level) AS total_enrollments,
SUM(CASE WHEN UPPER(f.business_level) = 'EXECUTIVE' THEN 1 else 0 end)
    AS executive_enrollments,
SUM(CASE WHEN UPPER(f.business_level) = 'PERSONAL' THEN 1 else 0 end)
    AS personal_enrollments,
SUM(CASE WHEN UPPER(f.business_level) = 'PREFERRED CUSTOMER' THEN 1 else 0 end)
    AS preferred_customer_enrollments,
IFNULL(cf.commission_paid, 0) AS commission_paid,
IFNULL(cf.retention_earned, 0) AS retention_earned,
COUNT(df.order_type) AS total_autoships,
IFNULL(a.consecutive_streak, 0) AS autoship_streak,
IFNULL(a.enrollment_date, "Not Enrolled") AS autoship_enrollment,
d.highest_rank
    FROM warehouse.distributor d
        LEFT JOIN warehouse.enrollment_detail_fact f ON d.distributor_id = f.distributor_id
        LEFT JOIN warehouse.country c ON d.country = c.name
             AND c.country_id = 185
        LEFT JOIN warehouse.autoship a ON d.distributor_id = a.distributor_id
        LEFT JOIN warehouse.order_detail_fact df ON d.distributor_id = df.distributor_id
            AND UPPER(order_type) = 'AUTOSHIP'
            AND date_id IN(SELECT date_id FROM warehouse.date
                WHERE DATE BETWEEN '2012-10-10'
                AND '2012-10-11' ORDER BY date DESC)
        LEFT JOIN warehouse.commission_detail_fact cf ON d.distributor_id = df.distributor_id
        LEFT JOIN db.commission_level_type_details cl ON d.highest_rank = cl.name
WHERE d.active = 1               
    AND cl.commission_level_type_detail_id IN (23)
GROUP BY distributor_id
ORDER BY first_name; 
4

3 に答える 3

2

この WHERE 句を JOIN 句に移動してみます。

AND cl.commission_level_type_detail_id IN (23)

この JOIN 句に追加します。

LEFT JOIN db.commission_level_type_details cl ON d.highest_rank = cl.name

この JOIN 句の場合:

LEFT JOIN warehouse.order_detail_fact df ON d.distributor_id = df.distributor_id
            AND UPPER(order_type) = 'AUTOSHIP'
            AND date_id IN(SELECT date_id FROM warehouse.date
                WHERE DATE BETWEEN '2012-10-10'
                AND '2012-10-11' ORDER BY date DESC)

このデータ構造*AND UPPER(order_type) = 'AUTOSHIP')*を「order_type」テーブルに正規化し、代わりにインデックス付き整数 ID を使用します。はるかに効率的です。

また、date_id を非正規化します (レコードの日付を正規化する理由がわかりません。ビジネス要件の一部が欠けている可能性があります)。同じテーブルに日付を入れてインデックスを作成し、MySQL が最も得意とすることを実行するだけです。WHERE 句に埋め込まれた SELECT はインデックス化されていないため、MySQL はそのデータを最適に処理できません。

実際のところ、INTEGER ではない JOIN 句と WHERE 句のすべてを正規化します。それらを整数 ID に変換します。これにより、パフォーマンス コストが大幅に削減されます。経験則として、DB サーバーに英数字インデックスのシークを要求することはありません。

思いついたら編集して投稿していきます。

お役に立てれば。幸運を。

于 2012-11-02T19:47:04.087 に答える
1

なぜ「インデックス以外」と言うのかわかりません。それは私が最適化を探し始める最初の場所でしょう。結合、WHERE句のフィルタリング、グループ化、および並べ替えに使用するすべてのフィールドには、インデックスが必要です。また、GROUPBYおよびORDERBYで使用されるフィールドに関連付けられたテーブルを明示的に定義する必要があります。

あなたはこのようなものを排除する必要があります

UPPER(order_type) = 'AUTOSHIP'

これらの値を結合、フィルタリング、グループ化に使用している場合、これにより、フィールドのインデックスが使用されなくなります。また、SELECTステートメントでこれらのUPPER関数呼び出しを使用すると、パフォーマンスが低下します(これらは、インデックスを使用しない場合のように、パフォーマンス的にはコストがかかりません)。データが適切にサニタイズされている場合は、これらは必要ありません。

おそらく、日付テーブルを内部結合し、メインのWHERE句に日付範囲フィルターを追加するだけで、そのサブ選択を排除することも検討する必要があります。同様に、結合フィールドとしてWHERE句に入る可能性のあるフィルターを使用している場合もあります。クエリを読みやすくするためだけに、適切なキーでテーブルを結合し、すべてのフィルタリングロジックをWHERE句に配置します。

スタースキーマデータウェアハウスを扱っているように見えるため、インデックスを最適化して副選択を削除した後でも、大量のデータがある場合は、クエリが遅くなる可能性があります。

于 2012-11-02T19:34:20.317 に答える