加入大表的表mydat
具有圍繞48.3M
記錄與定義:與主鍵
┌────────────────┬──────────────┬───────────┐
│ Column │ Type │ Modifiers │
├────────────────┼──────────────┼───────────┤
│ id │ bigint │ not null │
│ dt │ integer │ not null │
│ data │ real │ │
└────────────────┴──────────────┴───────────┘
Indexes:
"mydat_pkey" PRIMARY KEY, btree (id, dt)
對於每個對象所確定id
,大約有40記錄所表示由dt
時間字段。我們的目標是檢查連續記錄之間的變化模式,並且實現是將每個記錄與其下一個記錄相連,每個記錄基於每個id
基於dt
。查詢如下:
SELECT *
FROM mydat AS dat1
JOIN mydat AS dat2
ON dat1.id = dat2.id
AND dat1.dt = dat2.dt - 1;
查詢計劃如下。 合併加入被使用,並且它永遠運行。另外,我們可以看到行數427198811
嚴重高估。似乎postgresql沒有考慮到(id,dt)
的唯一性。
┌───────────────────────────────────────────────────────────────────────────────────────────────┐
│ QUERY PLAN │
├───────────────────────────────────────────────────────────────────────────────────────────────┤
│ Merge Join (cost=19919125.46..25466155.03 rows=247144681 width=222) │
│ Merge Cond: ((dat1.id = dat2.id) AND (dat1.dt = ((dat2.dt - 1)))) │
│ -> Sort (cost=9959562.73..10080389.92 rows=48330876 width=111) │
│ Sort Key: dat1.id, dat1.dt │
│ -> Seq Scan on mydat dat1 (cost=0.00..982694.76 rows=48330876 width=111) │
│ -> Materialize (cost=9959562.73..10201217.11 rows=48330876 width=111) │
│ -> Sort (cost=9959562.73..10080389.92 rows=48330876 width=111) │
│ Sort Key: dat2.id, ((dat2.dt - 1)) │
│ -> Seq Scan on mydat dat2 (cost=0.00..982694.76 rows=48330876 width=111) │
└───────────────────────────────────────────────────────────────────────────────────────────────┘
出於好奇,這裏是與自身幼稚加盟mydat
:
SELECT *
FROM mydat AS dat1
JOIN mydat AS dat2
ON dat1.id = dat2.id
AND dat1.dt = dat2.dt;
的查詢計劃是相似的:
┌───────────────────────────────────────────────────────────────────────────────────────────────┐
│ QUERY PLAN │
├───────────────────────────────────────────────────────────────────────────────────────────────┤
│ Merge Join (cost=19919125.46..27878413.41 rows=427198811 width=222) │
│ Merge Cond: ((dat1.id = dat2.id) AND (dat1.dt = dat2.dt)) │
│ -> Sort (cost=9959562.73..10080389.92 rows=48330876 width=111) │
│ Sort Key: dat1.id, dat1.dt │
│ -> Seq Scan on act_2003q1 dat1 (cost=0.00..982694.76 rows=48330876 width=111) │
│ -> Materialize (cost=9959562.73..10201217.11 rows=48330876 width=111) │
│ -> Sort (cost=9959562.73..10080389.92 rows=48330876 width=111) │
│ Sort Key: dat2.id, dat2.dt │
│ -> Seq Scan on act_2003q1 dat2 (cost=0.00..982694.76 rows=48330876 width=111) │
└───────────────────────────────────────────────────────────────────────────────────────────────┘
這樣的查詢計劃我迷惑不解。在這裏,我的問題是這些用例的最佳實踐是什麼?謝謝。
上述查詢在Windows上的Postgresql 9.5.3
和Linux上的9.4.6
上測試:結果類似。
鑑於@歐文的建議下,在滯後窗函數進行了測試,結果比初始合併連接方式要好得多:511524ms
完成查詢。正如Erwin指出的那樣,查詢與原始查詢不完全相同。特別是如果在dt
字段中存在間隙,那麼一些記錄將是不希望的。
這是一個例子,我發現表分區是有益的,因爲我使用的數據集比上面給出的例子大。問題的底線是postgresql使用磁盤對所有記錄進行排序,並且兩個查詢都不使用索引。
而不是'select * ...'嘗試'選擇id,dt ...' – Hogan
@Hogan,剛試過,沒有什麼區別。仍將合併與排序合併。 –
'我的目標是加入每條記錄和每個記錄的下一個記錄'。這不是一個目標,那是一個實現細節。你的實際目標是什麼?並且請*總是*包含您的Postgres版本。 –