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

重い結合を使用したMySQLクエリの最適化

    データがひどくクラスター化 されています 。

    InnoDBは、「近い」PKが物理的に近接している行を格納します。子テーブルは代理PKを使用するため、それらの行は事実上ランダムに格納されます。 「マスター」テーブルの特定の行の計算を行うときが来ると、DBMSは、子テーブルから関連する行を収集するために、あちこちをジャンプする必要があります。

    代理キーの代わりに、次のように、親のPKをリーディングエッジにして、より「自然な」キーを使用してみてください。

    score_adjustments:
        entry_id: INT(11), FOREIGN KEY (entries.id)
        created: DATETIME
        amount: INT(4)
        PRIMARY KEY (entry_id, created)
    
    rating_adjustments:
        entry_id: INT(11), FOREIGN KEY (entries.id)
        rating_no: INT(11)
        rating: DOUBLE
        PRIMARY KEY (entry_id, rating_no)
    

    注:これは、createdを前提としています。 の解像度は十分に良好で、rating_no entry_idごとに複数の評価を許可するために追加されました 。これは単なる例です。必要に応じてPKを変更できます。

    これにより、同じentry_idに属する行が「強制」されます 物理的に近接して保存されるため、SUMまたはAVGは、PK /クラスタリングキーの範囲スキャンだけで、I/Oをほとんど使用せずに計算できます。

    または(たとえば、クラスタリングをサポートしていないMyISAMを使用している場合)、カバー インデックスを使用したクエリ。クエリ中に子テーブルがまったく変更されないようにします。

    さらに、設計を非正規化し、現在の結果を親テーブルにキャッシュすることができます:

    • SUM(score_adjustments.amount)を物理フィールドとして保存し、score_adjustmentsから行が挿入、更新、または削除されるたびに、トリガーを介して調整します。 。
    • SUM(rating_adjustments.rating)を「S」として保存および COUNT(rating_adjustments.rating)を「C」として。行がrating_adjustmentsに追加されたとき 、それをSに追加し、Cをインクリメントします。実行時にS / Cを計算して、平均を取得します。更新と削除を同様に処理します。


    1. MariaDBでのFROM_UNIXTIME()のしくみ

    2. 月ごとの製品売上を比較するSQLクエリ

    3. mysqlクエリで複数の行の列の最大セットを見つける

    4. テーブルが作成されないsqliteandroid