【问题标题】:Read uncommited rows created by postgresql function?读取由 postgresql 函数创建的未提交的行?
【发布时间】:2017-12-23 01:37:15
【问题描述】:

我写了一个函数,根据参数在表mytable中创建一行,并返回创建行的id。

不幸的是,如果我使用下面的语句,SELECT 没有返回任何行,好像函数的事务与 SELECT 的事务不同。

SELECT * FROM mytable WHERE id = createComplexRow(...);

我的理解是我正在为 select 和 function 运行相同的事务,然后应该能够读取未提交的行。

我正在尝试使用 postgres 9.6

任何线索如何使它正常工作?

【问题讨论】:

    标签: postgresql function transactions read-uncommitted


    【解决方案1】:

    让我们看看发生了什么。

    CREATE TABLE mytable(
       id serial PRIMARY KEY,
       val timestamp with time zone NOT NULL
    );
    
    CREATE FUNCTION createcomplexrow() RETURNS integer
       LANGUAGE SQL AS
    'INSERT INTO mytable (val) VALUES (current_timestamp) RETURNING id';
    

    函数隐含VOLATILE,因为它修改了数据库。

    让我们插入几行:

    SELECT createcomplexrow();
    SELECT createcomplexrow();
    

    现在让我们试试你的说法:

    SELECT * FROM mytable WHERE id = createcomplexrow();
    
     id | val
    ----+-----
    (0 rows)
    

    确实,没有结果!

    但表中有新值:

    SELECT * FROM mytable;
    
     id |              val
    ----+-------------------------------
      1 | 2017-07-18 11:50:22.031922+02
      2 | 2017-07-18 11:50:23.640775+02
      3 | 2017-07-18 11:50:31.392773+02
      4 | 2017-07-18 11:50:31.392773+02
    (4 rows)
    

    要查看会发生什么,EXPLAIN 查询:

    EXPLAIN (COSTS off)
       SELECT * FROM mytable WHERE id = createcomplexrow();
    
                 QUERY PLAN
    -------------------------------------
     Seq Scan on mytable
       Filter: (id = createcomplexrow())
    (2 rows)
    

    PostgreSQL 扫描表,对于找到的每一行,它调用函数并将结果与​​行的id 进行比较。

    当它扫描带有id = 1 的行时,该函数将返回 3(并插入一行)。所以id = 1的行被跳过了。

    同样,id = 2 的行被跳过,id = 4 的新行被创建。

    现在为什么执行在这里停止而不是继续扫描两个新创建的行?

    这些lines from the documentation 有点解释:

    实际上,SELECT 查询会在查询开始运行时看到数据库的快照。 然而,SELECT 确实看到了在其自己的事务中执行的先前更新的效果,即使它们尚未提交。

    (强调我的)

    该语句看不到函数的效果,因为该函数不是在先前的更新中执行,而是在与SELECT相同的语句中执行。

    data modifying WITH queries 中也有同样的情况;您可能会发现阅读文档的那部分很有启发性。)

    实际上,您应该很高兴以这种方式处理它,否则您最终会陷入无限循环,不断将行插入表中。

    【讨论】:

    • 问题是您要达到的目标。你当然不想要一个无限循环,对吗?
    • 当然不是,实际用例更多的是UPDATE mytable SET specific WHERE id = createComplexRow(...),我想在常规sql中(不是在函数中)这样做
    • 好吧,我将使用DO 块来存储值并将请求分成两部分,谢谢
    • 您可以先输入行,然后在不同的 SQL 语句中更新它们,或者(更好)立即插入正确的值而不更新新行。另一个选项是 BEFORE 触发器,它修改应该插入的值。
    猜你喜欢
    • 1970-01-01
    • 2010-12-06
    • 1970-01-01
    • 1970-01-01
    • 2021-10-08
    • 1970-01-01
    • 2013-05-29
    • 2011-08-29
    • 2020-04-17
    相关资源
    最近更新 更多