【问题标题】:Is SELECT "faster" than function with nested INSERT?SELECT 是否比嵌套 INSERT 的函数“更快”?
【发布时间】:2020-04-18 23:54:45
【问题描述】:

我正在使用一个函数,如果该行不存在,则将其插入到表中,然后返回该行的 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);

要重现该问题,请传递表中尚不存在的任何整数。

【问题讨论】:

  • 这不是“速度”,它听起来像是事务可见性。考虑发布一个 full 示例,该示例还应显示 INSERT/UPDATE 关系。
  • 我试着做一个更小的例子,完整的读起来很无聊
  • @user2864740 完成了一个非常简单的例子,我希望它能给你一些背景

标签: sql postgresql concurrency upsert


【解决方案1】:

该示例在多个地方损坏。

  • EXECUTE 不需要动态 SQL。
  • 函数中的SELECT * 错误。
  • 您的表定义应该对(id) 有一个UNIQUEPRIMARY KEY 约束。
  • 最重要的是,最后的SELECT 语句肯定会失败。由于function is 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小提琴here

相关:

并发调用仍然存在竞争条件。如果发生这种情况,请考虑:

【讨论】:

    猜你喜欢
    • 2018-12-28
    • 2016-05-03
    • 2012-12-16
    • 2014-08-17
    • 2011-11-25
    • 1970-01-01
    • 2018-03-20
    • 1970-01-01
    • 2012-04-03
    相关资源
    最近更新 更多