sql - SELECT 是否比嵌套 INSERT 的函数“更快”?
问题描述
我正在使用一个函数,如果它不存在,则将行插入到表中,然后返回该行的 id。
每当我将函数放入SELECT
语句中时,表中尚不存在值,例如:
SELECT * FROM table WHERE id = function(123);
...它返回一个空行。但是,使用相同的值再次运行它将返回包含我想要查看的值的行。
为什么会这样?INSERT
奔跑落后于SELECT
速度吗?或者 PostgreSQL 是否在表不存在时对其进行缓存,并在下次运行时显示结果?
这是一个现成的示例,说明此问题是如何发生的:
CREATE TABLE IF NOT EXISTS test_table(
id INTEGER,
tvalue boolean
);
CREATE OR REPLACE FUNCTION test_function(user_id INTEGER)
RETURNS integer
LANGUAGE 'plpgsql'
AS $$
DECLARE
__user_id INTEGER;
BEGIN
EXECUTE format('SELECT * FROM test_table WHERE id = $1')
USING user_id
INTO __user_id;
IF __user_id IS NOT NULL THEN
RETURN __user_id;
ELSE
INSERT INTO test_table(id, tvalue)
VALUES (user_id, TRUE)
RETURNING id
INTO __user_id;
RETURN __user_id;
END IF;
END;
$$;
称呼:
SELECT * FROM test_table WHERE id = test_function(4);
要重现此问题,请传递表中尚不存在的任何整数。
解决方案
该示例在多个地方被破坏。
- 无需使用
EXECUTE
. SELECT *
在函数中是错误的。- 您的表定义应该有一个
UNIQUE
orPRIMARY KEY
约束(id)
。 最重要的
SELECT
是,最后的陈述必然会失败。由于该函数是VOLATILE
(必须是),它对表中的每个现有行进行一次评估。即使这有效,这也将是一场性能噩梦。但事实并非如此。就像@user2864740 评论的那样,可见性也存在问题。Postgres 根据函数的结果检查每一行现有的行,然后添加 1 行或更多行,并且这些行尚未在SELECT
正在操作的快照中。SELECT * FROM test_table WHERE id = test_function(4);
这会起作用(但见下文!):
CREATE TABLE test_table (
id int PRIMARY KEY --!
, tvalue bool
);
CREATE OR REPLACE FUNCTION test_function(_user_id int)
RETURNS test_table LANGUAGE sql AS
$func$
WITH ins AS (
INSERT INTO test_table(id, tvalue)
VALUES (_user_id, TRUE)
ON CONFLICT DO NOTHING
RETURNING *
)
TABLE ins
UNION ALL
SELECT * FROM test_table WHERE id = _user_id
LIMIT 1
$func$;
并将您的替换SELECT
为:
SELECT * FROM test_function(1);
db<>在这里摆弄
有关的:
并发调用仍然存在竞争条件。如果发生这种情况,请考虑:
推荐阅读
- html - ExtentReports:不在 log.info 消息中打印肥皂请求
- r - 如何在绘图饼图中硬编码显示变量的颜色?
- .net - 将 async 和 await 与 lambda 表达式一起使用?
- angular - 如何显示 amexio-d3-chart-donut?
- django - 我想根据用户信息动态改变django的形式
- python-3.x - 在 Python 二进制文件中包含 WinPcap
- c++ - 有没有更好的方法来添加两个智能指针?
- jquery - 数据表中未排序的结果
- r - 使用 R 从字符中删除停用词
- firebase - 谷歌云端硬盘手表变化的无服务器解决方案