時間と位置のデータを含む数千行のテーブルがあります。同じgroup_idを持つ行のタイムスタンプは同じです。このテーブルを「小」と呼びましょう。
Small Table:
id event_time group_id item_id position
1 '2018-06-21 18:35:01.631094+00' '123a' 1 '01010230...'
2 '2018-06-21 18:35:01.630881+00' '123a' 2 '01010044...'
3 '2018-06-18 10:35:01.630663+00' '321b' 1 '01015600...'
4 '2018-06-18 10:35:01.630305+00' '321b' 2 '01010031...'
同様のデータ列(時間、位置、データ1、データ2など)を持つ別のテーブル(テーブル "ビッグ")があります。このテーブルのタイムスタンプは連続的で、最初のテーブルと重複しており、8000万行以上あります。
Big Table:
id event_time Data1 position
1 '2018-06-21 18:45:01.631094+00' 'john' '01013000...'
2 '2018-06-21 18:41:01.630881+00' 'joe' '01016000...'
3 '2018-06-21 18:33:01.630663+00' 'john' '01017000...'
4 '2018-06-21 18:30:01.630305+00' 'rory' '01018000...'
2つのテーブルに地理空間インデックスと時間インデックスがあります。
私がしたいことは、BigとSmallの間で最も近い一致を見つけ、data1、data2、および空間と時間の違いを返すことです。つまり、「ジョン」がグループ「123a」のアイテム2に最も一致することを確認したいと思います。彼はそこから100 mと2分でしたが、「ロリー」はアイテム1に最も近かった(5分と1 kmまたはなんでも)。
私はこれに似たコマンドを試しましたが、遅すぎます。インデックスを使用していないようです。
SELECT
big.id,
small.id,
st_distance(big.position, small.position) as pos_delta,
(big.event_time, small.event_time) as time_delta,
big.data1,
small.item_id
FROM big, small
WHERE
(big.event_time - small.event_time) < '2 hours'
ORDER BY login_sar_vessel.position <-> login_pos_report.position
LIMIT 1
最初に大きなテーブルからgroup_idに関連付けられたタイムスタンプの前後に2時間データを選択し、距離が最も小さい(big.positionからsmall.position)大きな行を見つけて、各group_idに対して繰り返す方法はありますか?それは少し厄介なようです。
ああ、DBはpostgres 9.6とpostgis 2.4です。
あなたはこのようなものが欲しいと思います、
SELECT
big.id,
small.id,
ST_Distance(big.geom, small.geom),
big.event_time <-> small.event_time
FROM small
CROSS JOIN LATERAL (
SELECT *
FROM big
WHERE (small.event_time <-> big.event_time) < '2 hours'::interval
ORDER BY small.geom <-> big.geom
OFFSET 0
LIMIT 1
) AS big;
btree_Gist を使用して、timestmapとgeomの両方にこれらの両方を追加して、さらに楽しいことができます。
CREATE EXTENSION btree_Gist;
CREATE INDEX ON big USING Gist (event_time, geom);
CREATE INDEX ON small USING Gist (event_time, geom);
VACUUM FULL ANALYZE big;
VACUUM FULL ANALYZE small;
注:PostgreSQL 10にアップグレードすると、この作業負荷にmajorの違いが生じるため、並列処理が必要になります。
タイムスタンプのBRINインデックス、およびタイムスタンプによるクラスタリングも検討できます。その後、GISに移行します。または、インデックス付きの新しいMATERIALIZED VIEW
データの最後の1週間のみを含めます。
これが私が最終的に使用したオプションです。いくつかの一般的なテーブル式を使用して、必要な最小量にデータを削減します。いくつかの「ビッグ」データを「小さい」タイムスタンプの前後からフェッチするという考えです。次に、時間と空間で「小」ポイントに最も近い「大」ポイントを並べ替えて検索します。いくつかの作業が必要です:
--Get Small info
WITH small_cut AS
(SELECT * FROM
small
WHERE
group_id = 'abc123'
),
-- Get BIG info from before Small event
big_before as
(SELECT DISTINCT ON (Data1)
big.data1
big.position,
big.event_time,
(small_cut.event_time - big.event_time) as time_delta,
FROM
big,small_cut
WHERE
ST_Within(big.position, small_cut.bounding_geom)
AND
big.event_time > small_cut.event_timestamp - INTERVAL '1 Hours'
AND
big.event_time < small_cut.event_timestamp
ORDER BY data1, big.event_time DESC
),
big_after as
(SELECT DISTINCT ON (Data1)
big.data1
big.position,
big.event_time,
(small_cut.event_time - big.event_time) as time_delta,
FROM
big,small_cut
WHERE
ST_Within(big.position, small_cut.bounding_geom)
AND
big.event_time > small_cut.event_timestamp - INTERVAL '1 Hours'
AND
big.event_time < small_cut.event_timestamp
ORDER BY data1, big.event_time DESC
)
SELECT DISTINCT ON (small.item_id)
*
FROM Big, small_cut, big_before, big_after
WHERE
{some Id's are equal to other's}
私のDB構造が少し変更され、提供したダミーデータと100%インライン化されていないため、これが他の人にとってどれほど役立つかわかりません。