使用主键

时间:2016-07-28 20:37:53

标签: sql postgresql join

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时间字段表示。目标是检查连续记录之间的变化模式,并且实现是基于每个dt的{​​{1}}将每个记录与下一个记录连接起来。查询如下:

id

查询计划如下。使用 Merge-Join ,它需要永远运行。此外,我们可以看到行计数SELECT * FROM mydat AS dat1 JOIN mydat AS dat2 ON dat1.id = dat2.id AND dat1.dt = dat2.dt - 1; 被严重高估。似乎postgresql没有考虑427198811的唯一性。

(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;

这样的查询计划让我感到困惑。在这里,我的问题是这些用例的最佳实践是什么?感谢。

上述查询在Windows上的┌───────────────────────────────────────────────────────────────────────────────────────────────┐ │ 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) │ └───────────────────────────────────────────────────────────────────────────────────────────────┘ 和Linux上的Postgresql 9.5.3上进行了测试:结果类似。

鉴于@ Erwin的建议,测试了窗口函数的滞后,结果比初始合并连接方法好得多:9.4.6来完成查询。正如Erwin所指出的那样,查询与原始查询并不完全相同。特别是如果511524ms字段中存在间隙,则某些记录将是不需要的。

这是一个例子我发现表分区是有益的,因为我使用的数据集大于上面给出的例子。问题的根本在于postgresql使用磁盘对所有记录进行排序,并且根本不对两个查询使用索引。

2 个答案:

答案 0 :(得分:2)

如果您的实际目标是在每个结果行中保留private void addGameButton_Click(object sender, EventArgs e) { ControlCreator.DynamicInvoke(); } 的先前值,请使用window function

data

区别:仍然包含没有前任的行。你可能想要也可能不想要那样。要排除,请使用子查询:

SELECT *, lag(data) OVER (PARTITION BY id ORDER BY dt) AS last_data
FROM   mydat;

剩下的角落情况如下:如果SELECT * FROM ( SELECT *, lag(data) OVER (PARTITION BY id ORDER BY dt) AS last_data FROM mydat ) t WHERE last_data IS NOT NULL; 可以为NULL,我们无法区分真正的NULL值和未找到的'。因此,对于" not found"使用不同的不可能的默认值。案例如:

data

每个查询只需 顺序扫描

答案 1 :(得分:0)

我不太了解postgresql,但这对db2和mssql来说会很神奇

WITH get_em as
( SELECT id, dt
  FROM mydat AS dat1
  JOIN mydat AS dat2
    ON dat1.id = dat2.id
   AND dat1.dt = dat2.dt - 1
)
select mydat.*
from mydat
join get_em on ON mydat.id = get_em.id AND mydat.dt = get_em.dt