如何解决执行 SELECT 或 INSERT
我想在 PostgreSQL 中编写类似 CASE
语句的内容,以便我想从表中 SELECT
,如果没有找到,我想 INSERT
进入代替桌子。
我的示例表 allocated_ideas
如下所示:
challenge_id | user_id | idea_id | round | is_completed
-------------+------------+----------------+-------+-------------
'5d956daa' | '9afbca19' | '3798826ae522' | 5 | false
我想在 SQL 函数中做这样的事情:
SELECT EXISTS (
SELECT 1 from allocated_ideas ai
where ai.user_id = '9afbca19'
and ai.challenge_id = '5d956daa'
and ai.is_completed is false
如果此条件为真,则返回 idea_id
。否则,将值插入表中。伪代码:
if (above query is true) {
SELECT idea_id from allocated_ideas ai
where ai.user_id = '9afbca19'
and ai.challenge_id = '5d956daa'
and ai.is_completed is false
} else {
INSERT VALUES INTO allocated_ideas('67879','46578','978798',6,false)
}
解决方法
表上没有并发写入负载,您可以简单地:
WITH sel AS (
SELECT idea_id
FROM allocated_ideas
WHERE user_id = '9afbca19'
AND challenge_id = '5d956daa'
AND is_completed IS false
),ins AS (
-- spell out target columns!
INSERT INTO allocated_ideas (challenge_id,user_id,idea_id,round,is_completed)
SELECT '67879','46578','978798',6,false
WHERE NOT EXISTS (TABLE sel) -- only when sel is empty!
-- RETURNING idea_id -- unused
)
TABLE sel;
数据修改 CTE ins
总是执行到完成,即使它在最终的主命令中没有被引用。 (一个简单的 SELECT
在没有被引用的情况下不会被执行。)见:
如果您省略 INSERT
的目标列,它可能会无声地(或嘈杂地)中断!
您没有指定从 idea_id
返回新的 INSERT
。如果需要,您可能还希望能够区分案例。我为此添加了 op
列:
WITH sel AS (
-- like above,ins AS (
-- like above
RETURNING idea_id -- used now
)
SELECT 'sel' AS op,* FROM sel
UNION ALL
SELECT 'ins' AS op,* FROM ins;
查询只能返回单行,并且 INSERT
在任何情况下都会执行(如果 sel
找到一行,则不执行任何操作),因此我们不需要 LIMIT 1
。
通常情况下,可能存在并发写入负载,但您希望避免竞争条件。见:
,这是使用通用表表达式 (CTE) 的一种方法:
WITH sel AS (
SELECT challenge_id,is_completed
FROM allocated_ideas
WHERE user_id = '9afbca19' AND challenge_id = '5d956daa' AND is_completed = FALSE
),ins as (
INSERT INTO allocated_ideas (challenge_id,is_completed)
SELECT '67879',FALSE
WHERE NOT EXISTS (TABLE sel)
RETURNING *
)
SELECT * FROM sel
UNION ALL
SELECT * FROM ins
请注意,这仅在没有修改表的并发事务时有效。如果您希望并发写入,请先尝试使用 ON CONFLICT 的 INSERT。