更新具有来自同一表的最新相关 id 的表列
Posted
技术标签:
【中文标题】更新具有来自同一表的最新相关 id 的表列【英文标题】:UPDATE table column with latest related id from the same table 【发布时间】:2021-10-14 10:32:02 【问题描述】:我在 PostgreSQL 13 中有这张表:
CREATE TABLE candles (
id serial primary key,
day integer,
minute integer,
"open" integer,
high integer,
low integer,
"close" integer,
volume integer,
id_d1 integer,
);
CREATE INDEX candles_idx1 ON public.candles (day,minute);
我尝试更新字段id_d1
,它应该在同一时间包含前一天的id
:
UPDATE candles s2
SET id_d1 = (SELECT id FROM candles s
WHERE s.id<s2.id
AND s.day<s2.day
AND s.minute=s2.minute
ORDER BY s.id DESC
LIMIT 1);
对于较小的数据量,它运行良好。对于 80k 条记录,它会无休止地运行。
解释查询:
Update on candles s2 (cost=0.00..744027.57 rows=80240 width=68)
-> Seq Scan on candles s2 (cost=0.00..744027.57 rows=80240 width=68)
SubPlan 1
-> Limit (cost=0.29..9.25 rows=1 width=4)
-> Index Scan Backward using candles_pkey on candles s (cost=0.29..2347.34 rows=262 width=4)
Index Cond: (id < s2.id)
Filter: ((day < s2.day) AND (minute = s2.minute))
我也试过(WHERE 子句中没有 id):
EXPLAIN
UPDATE candles s2
SET id_d1 = (SELECT id FROM candles s
WHERE s.day<s2.day
AND s.minute=s2.minute
ORDER BY s.id DESC
LIMIT 1);
结果:
Update on candles s2 (cost=0.00..513040.75 rows=80240 width=68)
-> Seq Scan on candles s2 (cost=0.00..513040.75 rows=80240 width=68)
SubPlan 1
-> Limit (cost=0.29..6.37 rows=1 width=4)
-> Index Scan Backward using candles_pkey on candles s (cost=0.29..4784.85 rows=787 width=4)
Filter: ((day < s2.day) AND (minute = s2.minute))
我应该如何修改查询或架构以在合理的时间内运行它?
【问题讨论】:
您需要id
列吗?从功能上讲,您的主键似乎是 (day,minute)
- 为什么不坚持使用它,然后使用视图来实现 day, day-1
配对?
【参考方案1】:
提高性能的关键(尤其是对于您的原始查询)是具有倒排索引列的索引。在此期间,将其设为 UNIQUE
:
CREATE UNIQUE INDEX candles_idx1 ON public.candles (minute, day);
平等列优先。参见:
Multicolumn index and performance Is a composite index also good for queries on the first field? Working of indexes in PostgreSQL如果索引不能是UNIQUE
,您必须告诉我们更多关于可能的重复以及您打算如何打破平局的信息。
如果可以的话,考虑用它作为PK来替换id列(完全)。您可能需要在 (day, minute)
上添加一个索引 ...
在更新所有行时,使用FROM
clause 中的window function lag()
加入单个子查询以计算所有目标值应该(快得多) (而不是为每一行运行一个相关的子查询):
UPDATE candles c
SET id_d1 = c2.prev_id
FROM (
SELECT id, lag(id) OVER (PARTITION BY minute ORDER BY day) AS prev_id
FROM candles
) c2
WHERE c.id = c2.id
如果某些行已经拥有正确的id_d1
,请添加此行以避免代价高昂的空更新:
AND id_d1 IS DISTINCT FROM c2.prev_id
见:
How do I (or can I) SELECT DISTINCT on multiple columns?在更新所有行时,索引可能甚至不会用于新查询。
索引到位后,考虑将id_d1
从表中完全删除。存储功能相关的值往往不是一个好主意。使用lag()
即时计算它应该很便宜。然后该值始终自动更新。否则,您必须考虑如何使列保持最新 - 这可能很棘手。
【讨论】:
以上是关于更新具有来自同一表的最新相关 id 的表列的主要内容,如果未能解决你的问题,请参考以下文章