我想在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的记录,但这会导致我认为种族条件。
答案 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
UPDATE
,INSERT
和DELETE
语句设为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