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

ORDERBYの最適化

    これは非常に興味深いクエリです。その最適化中に、MySQLがどのように機能するかについての多くの新しい情報を発見して理解することができます。すべてを一度に詳細に書く時間があるかどうかはわかりませんが、徐々に更新することができます。

    なぜ遅いのか

    基本的に2つのシナリオがあります:クイック遅い

    クイック 事前定義された順序でテーブル上を歩き、おそらく同時に、他のテーブルから各行のIDごとにデータをすばやくフェッチするシナリオ。この場合、LIMIT句で指定された十分な行ができたらすぐに歩行を停止します。注文はどこから来ますか?テーブルにあるbツリーインデックスまたはサブクエリの結果セットの順序から。

    遅い 事前定義された順序がなく、MySQLがすべてのデータを一時テーブルに暗黙的に配置し、テーブルをあるフィールドで並べ替えて、 nを返す必要があるシナリオ LIMIT句の行。その一時テーブルに入力したフィールドのいずれかがTEXTタイプ(VARCHARではない)の場合、MySQLはそのテーブルをRAMに保持しようとせず、ディスク上でフラッシュして並べ替えます(したがって追加のIO処理)。

    最初に修正すること

    順序に従うことができるインデックスを作成できない状況は多くあります(たとえば、異なるテーブルの列をORDER BYする場合)。そのような状況での経験則は、MySQLが配置するデータを最小限に抑えることです。一時テーブルで。どうすればいいですか?サブクエリで行の識別子のみを選択し、IDを取得したら、IDをテーブル自体および他のテーブルに結合してコンテンツをフェッチします。つまり、注文のある小さなテーブルを作成してから、クイックシナリオを使用します。 (これは一般的なSQLとは少し矛盾しますが、SQLの各フレーバーには、そのようにクエリを最適化する独自の手段があります。)

    偶然にも、SELECT -- everything is ok here それは大丈夫ではない最初の場所なので、面白そうに見えます。

    SELECT p.*
        , u.name user_name, u.status user_status
        , c.name city_name, t.name town_name, d.name dist_name
        , pm.meta_name, pm.meta_email, pm.meta_phone
        , (SELECT concat("{", 
            '"id":"', pc.id, '",', 
            '"content":"', replace(pc.content, '"', '\\"'), '",', 
            '"date":"', pc.date, '",', 
            '"user_id":"', pcu.id, '",', 
            '"user_name":"', pcu.name, '"}"') last_comment_json 
            FROM post_comments pc 
            LEFT JOIN users pcu ON (pcu.id = pc.user_id) 
            WHERE pc.post_id = p.id
            ORDER BY pc.id DESC LIMIT 1) AS last_comment
    FROM (
        SELECT id
        FROM posts p
        WHERE p.status = 'published'
        ORDER BY 
            (CASE WHEN p.created_at >= unix_timestamp(now() - INTERVAL p.reputation DAY) 
                THEN +p.reputation ELSE NULL END) DESC, 
            p.id DESC
        LIMIT 0,10
    ) ids
    JOIN posts p ON ids.id = p.id  -- mind the join for the p data
    LEFT JOIN users u ON (u.id = p.user_id)
    LEFT JOIN citys c ON (c.id = p.city_id)
    LEFT JOIN towns t ON (t.id = p.town_id)
    LEFT JOIN dists d ON (d.id = p.dist_id)
    LEFT JOIN post_metas pm ON (pm.post_id = p.id)
    ;
    

    これが最初のステップですが、今でも、不要な行に対してこれらの役に立たないLEFTJOINSとjsonのシリアル化を行う必要がないことがわかります。 (GROUP BY p.idをスキップしました 、どのLEFT JOINが複数の行になるかわからないため、集計は行いません。

    まだ書いていません:

    • インデックス
    • CASE句を再定式化します(UNION ALLを使用)
    • おそらくインデックスを強制する


    1. countとgroupbyを使用したMySQLクエリ

    2. エラー1215:ON DELETE SET NULLを使用すると、外部キー制約を追加できません

    3. MySQLデータベース用の.netコア2.1MVCでの一時的な障害処理

    4. SQL Serverでグローバル変数を宣言する方法..?