Postgres函数优化
Postgres function optimisation
我是运行下面的函数。用小得多的 table 测试它按预期工作(18 行 - ~400 毫秒)。然而,当指向我的真实数据(315000 行)时,它是 运行 48 小时并且还在继续。这比我在线性外推下的预期要长得多。
- 有更好的方法吗?
- 有没有办法在仍然 运行 的情况下测试它是否正在做它应该做的事情?
有什么方法可以优化下面的功能吗?
DO
$do$
DECLARE r public.tablex%rowtype;
BEGIN
FOR r IN SELECT id FROM public.tablex
LOOP
IF (select cast((select trunc(random() * 6 + 1)) as integer) = 5) THEN
UPDATE public.tablex SET test='variable1' WHERE id = r.id;
ELSIF (select cast((select trunc(random() * 6 + 1)) as integer) = 6) THEN
UPDATE public.tablex SET test='variable2' WHERE id = r.id;
END IF;
END LOOP;
RETURN;
END
$do$;
尝试简单的更新:
UPDATE tablex
SET test = CASE trunc(random() * 6) + 1
WHEN 5 THEN 'variable1'
WHEN 6 THEN 'variable2'
ELSE test
END
;
估计至少要快50~200倍
明确表示您的 DO
声明不必要地昂贵。但还有更多。
概率
你的更新概率分布不均:
- 1/6 或所有行更新为
'variable1'
。
- 但只有5/36(
(1/6) * (5/6)
)更新为'variable2'
。
从你的代码的其余部分来看,我假设这是一个意外错误,你想要每个相等的 1/6 份额.
消毒
您可以简化为:
UPDATE tablex
SET test = CASE trunc(random() * 6)
WHEN float '4' THEN 'variable1'
WHEN float '5' THEN 'variable2'
ELSE test
END;
结果加1没有意义。而是比较 4
和 5
而不是 5
和 6
(或 3
和 1
- 这里没有区别)。
比较 double precision
(= float
) 常量而不是将表达式 trunc(random() * 6)
的 double precision
结果转换为integer
每行,就像在您的原始代码中一样。
好多了,但是仍然很低效。
高级
UPDATE tablex
SET test = CASE WHEN random() >= float '0.5' THEN 'variable1'
ELSE 'variable2' END
WHERE random() >= float '0.6666667';
100 % 等效(除非你有触发器 ON UPDATE
或一些奇特的设置)但 快得多 ,因为只有实际接收更新的行才会被触及。三分之二的行根本没有被触及。
请注意,这两个 random()
调用是完全独立的。
如果您想尽可能精确地了解三分之二的概率,请使用 2 / float '3.0'
而不是 float '0.6666667'
。但这确实是学术上的差异。
您可能希望 LOCK tablex IN ROW EXCLUSIVE MODE
在 运行 之前(在同一事务中)排除并发写入的竞争条件。 Details in the manual.
备选方案
如果 列 test
已经可以包含目标值之一 'variable1' 或 'variable2' (在很多情况下)这个更便宜,但是:
UPDATE tablex t
SET test = upd.val
FROM (
SELECT id, CASE WHEN random() >= float '0.5' THEN 'variable1'
ELSE 'variable2' END AS val
FROM tablex
WHERE random() >= float '0.6666667'
-- ORDER BY id -- the last two lines only to defend against ...
-- FOR UPDATE -- ... concurrent writes and possible deadlocks
) upd
WHERE t.id = upd.id
AND t.test IS DISTINCT FROM upd.val;
避免更多的空更新。
如果 test
被定义为 NOT NULL
你可以简化为:
AND t.test <> upd.val;
比较这个相关答案的最后一章:
How do I (or can I) SELECT DISTINCT on multiple columns?
只有最后一个变体需要 id
上的索引才能更快。应该被主键约束覆盖。
我是运行下面的函数。用小得多的 table 测试它按预期工作(18 行 - ~400 毫秒)。然而,当指向我的真实数据(315000 行)时,它是 运行 48 小时并且还在继续。这比我在线性外推下的预期要长得多。
- 有更好的方法吗?
- 有没有办法在仍然 运行 的情况下测试它是否正在做它应该做的事情?
有什么方法可以优化下面的功能吗?
DO
$do$
DECLARE r public.tablex%rowtype;
BEGIN
FOR r IN SELECT id FROM public.tablex
LOOP
IF (select cast((select trunc(random() * 6 + 1)) as integer) = 5) THEN
UPDATE public.tablex SET test='variable1' WHERE id = r.id;
ELSIF (select cast((select trunc(random() * 6 + 1)) as integer) = 6) THEN
UPDATE public.tablex SET test='variable2' WHERE id = r.id;
END IF;
END LOOP;
RETURN;
END
$do$;
尝试简单的更新:
UPDATE tablex
SET test = CASE trunc(random() * 6) + 1
WHEN 5 THEN 'variable1'
WHEN 6 THEN 'variable2'
ELSE test
END
;
估计至少要快50~200倍
DO
声明不必要地昂贵。但还有更多。
概率
你的更新概率分布不均:
- 1/6 或所有行更新为
'variable1'
。 - 但只有5/36(
(1/6) * (5/6)
)更新为'variable2'
。
从你的代码的其余部分来看,我假设这是一个意外错误,你想要每个相等的 1/6 份额.
消毒
您可以简化为:
UPDATE tablex
SET test = CASE trunc(random() * 6)
WHEN float '4' THEN 'variable1'
WHEN float '5' THEN 'variable2'
ELSE test
END;
结果加1没有意义。而是比较
4
和5
而不是5
和6
(或3
和1
- 这里没有区别)。比较
double precision
(=float
) 常量而不是将表达式trunc(random() * 6)
的double precision
结果转换为integer
每行,就像在您的原始代码中一样。
好多了,但是仍然很低效。
高级
UPDATE tablex
SET test = CASE WHEN random() >= float '0.5' THEN 'variable1'
ELSE 'variable2' END
WHERE random() >= float '0.6666667';
100 % 等效(除非你有触发器 ON UPDATE
或一些奇特的设置)但 快得多 ,因为只有实际接收更新的行才会被触及。三分之二的行根本没有被触及。
请注意,这两个
random()
调用是完全独立的。如果您想尽可能精确地了解三分之二的概率,请使用
2 / float '3.0'
而不是float '0.6666667'
。但这确实是学术上的差异。您可能希望
LOCK tablex IN ROW EXCLUSIVE MODE
在 运行 之前(在同一事务中)排除并发写入的竞争条件。 Details in the manual.
备选方案
如果 列 test
已经可以包含目标值之一 'variable1' 或 'variable2' (在很多情况下)这个更便宜,但是:
UPDATE tablex t
SET test = upd.val
FROM (
SELECT id, CASE WHEN random() >= float '0.5' THEN 'variable1'
ELSE 'variable2' END AS val
FROM tablex
WHERE random() >= float '0.6666667'
-- ORDER BY id -- the last two lines only to defend against ...
-- FOR UPDATE -- ... concurrent writes and possible deadlocks
) upd
WHERE t.id = upd.id
AND t.test IS DISTINCT FROM upd.val;
避免更多的空更新。
如果 test
被定义为 NOT NULL
你可以简化为:
AND t.test <> upd.val;
比较这个相关答案的最后一章:
How do I (or can I) SELECT DISTINCT on multiple columns?
只有最后一个变体需要
id
上的索引才能更快。应该被主键约束覆盖。