加入大表的表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版本。 –