仅当行不存在时才在PL / pgSQL中运行SQL语句

时间:2017-01-02 17:18:11

标签: sql postgresql stored-procedures plpgsql sql-insert

我想在Postgres 9.6的PL / pgSQL函数中做这样的事情:

/emulated/home/...

但是,我不知道如何确定第一个INSERT INTO table1 (id, value) VALUES (1, 'a') ON CONFLICT DO NOTHING; --- If the above statement didn't insert a new row --- because id of 1 already existed, --- then run the following statements INSERT INTO table2 (table1_id, value) VALUES (1, 'a'); UPDATE table3 set (table1_id, time) = (1, now()); 是否实际插入了新行,或者INSERT是否已完成。

我可以在函数的开头做一个ON CONFLICT DO NOTHING,看看在运行所有SQL语句之前SELECT中是否存在id为1的记录,但这会导致我认为种族条件。

4 个答案:

答案 0 :(得分:4)

对于plpgsql函数,请使用special variable FOUND

CREATE FUNCTION foo(int, text)
 RETURNS void AS
$$
BEGIN
   INSERT INTO table1 (id, value) VALUES ($1, $2) ON CONFLICT DO NOTHING;

   IF NOT FOUND THEN
      INSERT INTO table2 (table1_id, value) VALUES ($1, $2);
      UPDATE table3 set (table1_id, time) = ($1, now())
      WHERE  ????;  -- you surely don't want to update all rows in table3
   END IF;
END
$$

呼叫:

SELECT foo(1, 'a');
如果FOUND实际上没有插入任何行,则

INSERT设置为 false

The manual about the ON CONFLICT Clause:

  

ON CONFLICT DO NOTHING只是避免插入一行   替代行动。

The manual about Obtaining the Result Status

  

UPDATEINSERTDELETE语句设为FOUND如果至少有一个为真   row受影响,如果没有行受影响,则为false。

要清楚,如果table1 中的行已经存在,则会运行后面的语句,因此不会插入新行。 (就像你要求的那样,但与你的问题标题相反。)

如果您只想检查是否存在行:

竞争条件?

如果同一事务中的后续命令依赖table1中的现有行(例如,使用FK),那么您将要锁定它以防止并发事务在此期间删除或更新它。一种方法:使用DO NOTHING代替DO UPDATE,而不是 实际更新行。该行仍处于锁定状态:

INSERT INTO table1 AS t (id, value)
VALUES ($1, $2)
ON     CONFLICT (id) DO UPDATE  -- specify unique column(s) or constraint / index
SET    id = t.id WHERE FALSE;   -- never executed, but locks the row

显然,如果您可以排除可能以冲突方式删除或更新同一行的并发事务,则该问题不存在。

详细说明:

答案 1 :(得分:3)

Postgres有returning子句和CTE来做你想做的事:

WITH t1 as (
      INSERT INTO table1 (id, value)
          VALUES (1, 'a')
          ON CONFLICT DO NOTHING
          RETURNING *
     ),
     t2 as (
      INSERT INTO table2 (table1_id, value) 
          SELECT id, value 
          FROM (SELECT 1 as id, 'a' as value) t
          WHERE NOT EXISTS (SELECT 1 FROM t1) 
   )
UPDATE table3
    set (table1_id, time) = (1, now())
    WHERE NOT EXISTS (SELECT 1 FROM t1);

update看起来很奇怪,因为它会更新table3中的所有行。

答案 2 :(得分:1)

也许你的意思是这样的?

INSERT INTO table1 (id, value) VALUES (1, 'a') ON CONFLICT DO NOTHING;
--- If the above statement didn't insert a new row
---   because id of 1 already existed, 
---   then run the following statements

affected_rows := SQL%ROWCOUNT;

IF affected_rows = 0 THEN
    INSERT INTO table2 (table1_id, value) VALUES (1, 'a');
    UPDATE table3 set (table1_id, time) = (1, now());
END IF

答案 3 :(得分:0)

最简单可靠的方法是使用特殊变量FOUND,这样:

INSERT INTO table1 (id, value) values (1, ‘a’) on conflict do nothing;

如果发现那么

- 成功

ELSE

- 失败

END IF;

以下是诊断声明的文档 https://www.postgresql.org/docs/9.6/static/plpgsql-statements.html