为什么当单个查询同时进行写入和读取时,数据库无法读取新插入的数据?

问题描述 投票:0回答:1
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,请指出我的问题是否过于边缘情况而与实际相关

sql postgresql transactions
1个回答
0
投票

在 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);
© www.soinside.com 2019 - 2024. All rights reserved.