DROP TABLE IF EXISTS A;
CREATE TABLE a (id int);
CREATE or replace FUNCTION insert_and_return(int)
RETURNS int AS $$
BEGIN
INSERT INTO a VALUES ($1);
RETURN $1;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM insert_and_return(10),A AS y;
上面的代码通过函数在 SELECT 中插入数据有副作用。
预计
插入并返回 | id |
---|---|
10 | 10 |
我期望
insert_and_return
在 FROM insert_and_return(10),A
执行交叉联接时插入新行,然后 SELECT 看到它并在结果中返回它。
实际:
空白结果集
问题:
为什么我看不到数据?我必须再次选择才能看到它。
我认为 SELECT 总是看到比实际少 1 行。
我希望了解幕后事件的顺序,数据是否已经插入但以某种方式从 SELECT 中隐藏,或者在执行 SELECT 时尚未插入。
相关问题
这个问题与隔离级别有什么关系吗?还是无关紧要的,因为隔离级别仅适用于多个事务?
为什么可以使用 CASE 或类似
SELECT price, price * 0.9
的模式即时生成数据,但在这里不起作用?
我尝试使用 CTE 创建分离,但看到了相同的行为,我只看到运行整个查询低于第二次的数据。 这可以用与上述相同的理由来解释吗,还是 CTE 有自己的注意事项?
WITH inserted_data AS (
INSERT INTO a (id) VALUES (10)
RETURNING id
)
SELECT * FROM inserted_data,A;
为什么其他帮助失败了
我在网上找到的所有来源都讨论了多个事务的并发读/写,我认为这些都是无关紧要的,因为这是单个隐式事务中的单个查询。
第一个查询的来源
我从这篇文章中调整了它,重点关注负载平衡而不是我的问题:https://www.cybertec-postgresql.com/en/why-select-from-table-is-not-a-read,请指出我的问题是否过于边缘情况而与实际相关
在 PostgreSQL 中,您观察到的行为是由于查询中语句的执行顺序造成的。当你跑步时:
SELECT * FROM insert_and_return(10), A AS y;
函数执行:首先调用insert_and_return(10)函数,将值10插入到表A中。
表 A 扫描:执行该函数后,PostgreSQL 尝试从 A(别名为 y)检索记录。
然而,PostgreSQL 的事务隔离会阻止函数的插入操作立即对查询可见。这是因为 PostgreSQL 在插入任何实际行之前有效地为查询的两个部分构建执行计划。由于 PostgreSQL 中的可见性规则,插入的行不可用于同一查询,该规则将单个语句中的数据修改与数据读取分开。
DROP TABLE IF EXISTS a;
CREATE TABLE a (id int);
CREATE OR REPLACE FUNCTION insert_and_return_read(val int)
RETURNS TABLE (inserted_id int, existing_id int) AS $$
BEGIN
RETURN QUERY
WITH ins AS (
INSERT INTO a (id) VALUES (val) RETURNING id
)
SELECT ins.id AS inserted_id, a.id AS existing_id
FROM ins
LEFT JOIN a ON TRUE; -- This will show the newly inserted value alongside any existing values in 'a'
END;
$$ LANGUAGE plpgsql;
-- Call the function
SELECT * FROM insert_and_return_read(10);