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

個々の識別子の最新の行にアクセスする適切な方法は?

    これは、この投稿で言及されているクエリのパフォーマンスの簡単な比較です。

    現在の設定:

    テーブルcore_message test_boatsには10,904,283行あり、60,740行あります。 (またはcore_messageの60,740個の異なるmmsi 。

    そして、私はPostgreSQL11.5を使用しています

    インデックスのみのスキャンを使用したクエリ:

    1)DISTINCT ONを使用する :

    SELECT DISTINCT ON (mmsi) mmsi 
    FROM core_message;
    

    2)RECURSIVEを使用する LATERALを使用 :

    WITH RECURSIVE cte AS (
       (
       SELECT mmsi
       FROM   core_message
       ORDER  BY mmsi
       LIMIT  1
       )
       UNION ALL
       SELECT m.*
       FROM   cte c
       CROSS  JOIN LATERAL (
          SELECT mmsi
          FROM   core_message
          WHERE  mmsi > c.mmsi
          ORDER  BY mmsi
          LIMIT  1
          ) m
       )
    TABLE cte;
    

    3)LATERALで追加のテーブルを使用する :

    SELECT a.mmsi
    FROM test_boats a
    CROSS JOIN LATERAL(
        SELECT b.time
        FROM core_message b
        WHERE a.mmsi = b.mmsi
        ORDER BY b.time DESC
        LIMIT 1
    ) b;
    

    インデックスのみのスキャンを使用しないクエリ:

    4)DISTINCT ONを使用する mmsi,time DESCを使用 INDEX

    SELECT DISTINCT ON (mmsi) * 
    FROM core_message 
    ORDER BY mmsi, time desc;
    

    5)DISTINCT ONを使用する 後方mmsi,timeを使用 UNIQUE CONSTRAINT

    SELECT DISTINCT ON (mmsi) * 
    FROM core_message 
    ORDER BY mmsi desc, time desc;
    

    6)RECURSIVEを使用する LATERALを使用 およびmmsi,time DESC INDEX

    WITH RECURSIVE cte AS (
       (
       SELECT *
       FROM   core_message
       ORDER  BY mmsi , time DESC 
       LIMIT  1
       )
       UNION ALL
       SELECT m.*
       FROM   cte c
       CROSS  JOIN LATERAL (
          SELECT *
          FROM   core_message
          WHERE  mmsi > c.mmsi
          ORDER  BY mmsi , time DESC 
          LIMIT  1
          ) m
       )
    TABLE cte;
    

    7)RECURSIVEを使用する LATERALを使用 および後方mmsi,time UNIQUE CONSTRAINT

    WITH RECURSIVE cte AS (
    
       (
    
       SELECT *
       FROM   core_message
       ORDER  BY mmsi DESC , time DESC 
       LIMIT  1
       )
       UNION ALL
       SELECT m.*
       FROM   cte c
       CROSS  JOIN LATERAL (
          SELECT *
          FROM   core_message
          WHERE  mmsi < c.mmsi
          ORDER  BY mmsi DESC , time DESC 
          LIMIT  1
          ) m
       )
    TABLE cte;
    

    8)LATERALで追加のテーブルを使用する :

    SELECT b.*
    FROM test_boats a
    CROSS JOIN LATERAL(
        SELECT b.*
        FROM core_message b
        WHERE a.mmsi = b.mmsi
        ORDER BY b.time DESC
        LIMIT 1
    ) b;
    

    最後のメッセージに専用のテーブルを使用する:

    9)これが私の最初の解決策であり、最後のメッセージのみを含む個別のテーブルを使用しています。このテーブルは、新しいメッセージが到着すると入力されますが、次のように作成することもできます:

    CREATE TABLE core_shipinfos AS (
        WITH RECURSIVE cte AS (
           (
           SELECT *
           FROM   core_message
           ORDER  BY mmsi DESC , time DESC 
           LIMIT  1
           )
           UNION ALL
           SELECT m.*
           FROM   cte c
           CROSS  JOIN LATERAL (
              SELECT *
              FROM   core_message
              WHERE  mmsi < c.mmsi
              ORDER  BY mmsi DESC , time DESC 
              LIMIT  1
              ) m
           )
        TABLE cte);
    

    次に、最新のメッセージを取得するリクエストはそれと同じくらい簡単です:

    SELECT * FROM core_shipinfos;
    

    結果:

    複数のクエリの平均(速いクエリの場合は約5):

    1)9146ミリ秒
    2)728ミリ秒
    3)498ミリ秒

    4)51488ミリ秒
    5)54764ミリ秒
    6)729ミリ秒
    7)778ミリ秒
    8)516ミリ秒

    9)15ミリ秒

    結論:

    専用のテーブルソリューションについてはコメントせず、最後までそれを維持します。

    追加のテーブル(test_boats )ソリューションは間違いなくここで勝者ですが、RECURSIVE ソリューションもかなり効率的です。

    DISTINCT ONのパフォーマンスには大きなギャップがあります インデックスのみのスキャンを使用し、それを使用しないスキャンを使用しますが、他の効率的なクエリのパフォーマンスの向上はかなり小さいです。

    これらのクエリがもたらす主な改善点は、core_message全体をループする必要がないという事実であるため、これは理にかなっています。 テーブルですが、一意のmmsiのサブセットのみです これは、core_messageと比較して大幅に小さい(60K +) テーブルサイズ(10M +)

    追記として、UNIQUE CONSTRAINTを使用したクエリのパフォーマンスは大幅に向上していないようです。 mmsi,time DESCを削除した場合 INDEX 。しかし、そのインデックスを削除すると、もちろんスペースを節約できます(このインデックスは現在328MBかかります)

    専用テーブルソリューションについて:

    core_messageに保存されている各メッセージ テーブルには、位置情報(位置、速度、船首方位など)と船の情報(名前、コールサイン、寸法など)、および船の識別子(mmsi)の両方が含まれています。

    私が実際にやろうとしていることについてもう少し背景を説明するために、AISプロトコル

    そのため、私が取得したすべての一意のmmsiは、このプロトコルを介して取得しました。事前定義されたリストではありません。 AISを使用して世界中のすべての船を入手するまで、新しいMMSIを追加し続けます。

    その文脈では、最後に受信したメッセージとして船の情報を含む専用のテーブルが理にかなっています。

    RECURSIVEで見たようなテーブルの使用を避けることができました 解決策ですが...専用テーブルはこのRECURSIVEよりも50倍高速です 解決策。

    その専用テーブルは、実際にはtest_boatに似ています。 mmsiだけではない詳細情報を含むテーブル 分野。そのまま、mmsiのテーブルを持つ core_messageの最後の情報がすべて含まれるフィールドまたはテーブルのみ テーブルは私のアプリケーションに同じ複雑さを追加します。

    結局、私はこの専用のテーブルに行くと思います。それは私に無敵のスピードを与えます、そして私はまだLATERALを使う可能性があります core_messageのトリック 、これにより柔軟性が向上します。



    1. MySQLは列全体からすべての空白を削除します

    2. MySQL区切り文字の構文エラー

    3. MySQLは浮動小数点加算の数学計算をどのように行いますか?

    4. DockerコンテナでのSQLスクリプトの実行