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