1

我有一个有趣的问题,我和我的同事现在困扰了一段时间。

我在 PostgreSQL-8.3 中有一个 PL/pgSQL 函数(对于那个旧版本,我无法更改),它执行以下四件事:

  1. 从序列中获取新的序列号 (ID)
  2. 将几条记录插入到具有该序列号的表中
  3. 发送一个插入发生的通知信号
  4. 将 ID 返回给调用者。

简化功能:

CREATE OR REPLACE FUNCTION add_entry(_user_name text, _visible_attr integer[])
  RETURNS bigint AS
$BODY$
  DECLARE
    user_name text := '#' || $1;
    user_id bigint;
  BEGIN

    -- get the ID from the sequence
    SELECT nextval('my_sequence') INTO user_id;

    -- insert the name (and some other data not shown here) 5x
    FOR item IN 1..5
    LOOP
      INSERT INTO mytable
        (id,index,username,visible)
        VALUES (user_id,item,user_name,$2[item]);
    END LOOP;

    -- send notify that an insertion took place
    notify my_notify;

    RETURN user_id;
  END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

所以,我的同事从他的应用程序中调用了这个函数。他获取返回的 ID 并在他的应用程序中使用另一个线程(DB 池)来调用一个函数,该函数将返回先前使用该 ID 插入的数据。但是,这在第一次不起作用。只有通过第二个请求,他才能选择数据。当函数已经返回时,似乎 INSERT 还没有完成?!

我们检查了多次,数据将正确插入到表中,但不知何故,它的可用速度不如返回值(序列中的 ID)可用!为什么呢?

更新:错误的假设

我进一步检查并将示例简化为一个真正显示问题的简单查询:

select * from mytable where id = (select add_entry('MyTestUser'));

此查询不返回任何行。但是,如果我分两个步骤执行此操作,我可以选择使用 add_entry 函数插入的数据。

我不知道我做错了什么或如何加快插入速度......

4

1 回答 1

1

来自 8.3 手册

实际上,SELECT 查询会在查询开始运行的那一刻看到数据库的快照

由于更新是在select自身中完成的,因此不会看到插入的行。

http://www.postgresql.org/docs/8.3/static/tutorial-transactions.html

将函数更改为 return setof mytable。它可以是纯 SQL。要更改返回类型,必须先删除函数

drop function add_entry(text);

create or replace function add_entry (_user_name text, _visible_attr integer[])
returns setof mytable as $body$

    notify my_notify;
    with ins as (
        insert into mytable (id, index, username, visible)
        select user_id, item, '#' || $1, $2[item]
        from
            generate_series(1, 5) g(item)
            cross join
            (values (nextval('my_sequence'))) s(user_id)
        returning *
    )
    select * from ins;

$body$ language sql volatile;

通知必须在函数返回任何内容之前发生。就好像插入失败事务回滚包括通知一样,这不是问题。像这样称呼它

select * from add_entry('MyTestUser');

选择将不会看到修改后的表,而是返回的mytable行。

如果需要该功能,plpgsql则使用return query

create or replace function add_entry (_user_name text, _visible_attr integer[])
returns setof mytable as $body$
begin
    notify my_notify;
    return query
        insert into mytable (id, index, username, visible)
        select user_id, item, '#' || $1, $2[item]
        from
            generate_series(1, 5) g(item)
            cross join
            (values (nextval('my_sequence'))) s(user_id)
        returning *
    ;
end;
$body$ language plpgsql volatile;
于 2014-07-24T09:10:24.350 回答