sql >> データベース >  >> RDS >> Mysql

MySQLの巨大なテーブルJOINはデータベースを崩壊させます

    300k行は巨大なテーブルではありません。 3億行のテーブルが頻繁に見られます。

    クエリの最大の問題は、相関サブクエリを使用しているため、行ごとにサブクエリを再実行する必要があることです。 外側のクエリで。

    多くの場合、すべてを実行する必要はありません。 1つのSQLステートメントでの作業。それをいくつかのより単純なSQLステートメントに分割することには利点があります:

    • コーディングが簡単です。
    • 最適化が容易です。
    • デバッグが簡単です。
    • 読みやすい。
    • 新しい要件を実装する必要がある場合、またはいつ実装する必要がある場合でも、保守が容易になります。

    購入数

    SELECT customer, COUNT(sale) AS number_of_purchases
    FROM sales 
    GROUP BY customer;
    

    このクエリには、sales(customer、sale)のインデックスが最適です。

    最終購入額

    これはgreatest-n-per-group です。 頻繁に発生する問題。

    SELECT a.customer, a.sale as max_sale
    FROM sales a
    LEFT OUTER JOIN sales b
     ON a.customer=b.customer AND a.dates < b.dates
    WHERE b.customer IS NULL;
    

    つまり、行aを一致させてください 架空の行にb 同じ顧客とより大きな日付があります。そのような行が見つからない場合は、a その顧客にとって最大の日付が必要です。

    このクエリには、sales(customer、dates、sale)のインデックスが最適です。

    その最大の日に顧客に対して複数の販売がある可能性がある場合、このクエリは顧客ごとに複数の行を返します。ネクタイを壊すには、別の列を見つける必要があります。自動インクリメントの主キーを使用する場合は、一意であることが保証されており、時系列で増加する傾向があるため、タイブレーカーとして適しています。

    SELECT a.customer, a.sale as max_sale
    FROM sales a
    LEFT OUTER JOIN sales b
     ON a.customer=b.customer AND (a.dates < b.dates OR a.dates = b.dates and a.id < b.id)
    WHERE b.customer IS NULL;
    

    正の値の場合の購入総額

    SELECT customer, SUM(sale) AS total_purchases
    FROM sales
    WHERE sale > 0
    GROUP BY customer;
    

    このクエリには、sales(customer、sale)のインデックスが最適です。

    -1の代わりにNULLを使用して、不足している販売値を示すことを検討する必要があります。 SUM()やCOUNT()などの集計関数はNULLを無視するため、sale<0の行を除外するためにWHERE句を使用する必要はありません。

    Re:コメント

    2012年第4四半期の上位5社

    SELECT customer, SUM(sale) AS total_purchases
    FROM sales
    WHERE (year, quarter) = (2012, 4) AND sale > 0
    GROUP BY customer
    ORDER BY total_purchases DESC
    LIMIT 5;
    

    実際のデータに対してテストしたいのですが、このクエリには売上(年、四半期、顧客、売上)のインデックスが最適だと思います。

    合計購入数が5を超える顧客の最終購入

    SELECT a.customer, a.sale as max_sale
    FROM sales a
    INNER JOIN sales c ON a.customer=c.customer
    LEFT OUTER JOIN sales b
     ON a.customer=b.customer AND (a.dates < b.dates OR a.dates = b.dates and a.id < b.id)
    WHERE b.customer IS NULL
    GROUP BY a.id
    HAVING COUNT(*) > 5;
    

    上記の他の最大のグループごとのクエリと同様に、sales(customer、dates、sale)のインデックスがこのクエリに最適です。おそらく、joinとgroup byの両方を最適化できないため、一時的なテーブルが発生します。ただし、少なくとも、一時テーブルは多数ではなく1つしか実行されません。

    これらのクエリは十分に複雑です。 すべてを与えることができる単一のSQLクエリを作成しようとしないでください。 これらの結果の。ブライアン・カーニハンからの古典的な引用を覚えておいてください:



    1. 郵便番号をデータベースに保存する必要があります。カラムの大きさはどれくらいですか?

    2. Oracle DMPファイルの使用方法は?

    3. dayname(curdate())codeigniterphpで機能していません

    4. Ubuntu20.04へのMySQLのインストールと構成