これは、この投稿で言及されているクエリのパフォーマンスの簡単な比較です。
現在の設定:
テーブル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のトリック 、これにより柔軟性が向上します。