我正在尝试在 Postgres 12.5 中做一些事情,但我要么遗漏了一个细节,要么完全追求一个有缺陷的策略。我有一个简单的查找表,其中包含一些较长的字符串和扩展的详细信息。当数据流入主表时,我想将这些字符串转换为一个简单的 int 作为键。我想我可以将查找保存在一个表中,然后使用 INSERT....ON CONFLICT DO NOTHING...RETURNING
来获得这些结果:
- 如果查找表中不存在该行,则创建它并返回新的
id
。 <-- 这行得通。 - 如果该行确实存在,则不要覆盖它并返回新的
id
。 <-- 未返回id
。
第二种情况更为常见。很有可能,系统会在新值到达查找表之间运行数天或数周。我更喜欢果断地执行 INSERT
以避免并发问题或唯一冲突。然而,RETURNING
并没有像我希望的那样工作。我检查了文档,清楚地记录了我所看到的行为。而且,考虑一下,很有意义。因此,继续前进,编写一个函数来测试结果并发出 SELECT id FROM pgconfig_target WHERE...
以恢复现有的 id
是否更有意义,还是其他一些更直接的策略?
作为背景,主表是一个 push_audit_log
总结了来 self 们客户的 insert
调用,包括时间、记录计数和其他详细信息。这些记录将会很多。这就是为什么我宁愿将字符串转移到另一个表。
这是查找表设置的简短版本:
--------------------------------------
-- Define table
------------------------------------
BEGIN;
DROP TABLE IF EXISTS dba.pgconfig_target CASCADE;
CREATE TABLE IF NOT EXISTS dba.pgconfig_target (
id int2 GENERATED ALWAYS AS IDENTITY,
schema_name citext NOT NULL DEFAULT NULL,
target_name citext NOT NULL DEFAULT NULL,
target_path citext NOT NULL DEFAULT NULL
);
ALTER TABLE dba.pgconfig_target
OWNER TO user_change_structure;
COMMIT;
------------------------------------
-- Build indexes
------------------------------------
CREATE UNIQUE INDEX pgconfig_target_unique_ix_btree
ON dba.pgconfig_target (schema_name, target_name, target_path);
这是我正在试验的 INSERT
:
INSERT INTO pgconfig_target (schema_name, target_name, target_path)
VALUES ('hello','world','passthrough.hello.world')
ON CONFLICT (schema_name,target_name,target_path) do nothing
RETURNING id;
感谢您的建议。
#稍后# 我想使用像列出的(曾经有用的)GMB 这样的建议,但似乎无法让它在第一次运行时工作。我很好奇为什么,因为我不理解有关命令或执行的某些内容。
我尝试了一些 scratch 函数,它确实有效,但如果可能的话,纯 SQL 语句解决方案似乎更好一些。这是函数:
CREATE OR REPLACE FUNCTION ascendco.pgconfig_target_add_if_missing (
s_name citext,
t_name citext,
t_path citext
)
RETURNS integer AS
$BODY$
DECLARE
id_out integer = 0;
BEGIN
-- Insert the value, if it's missing.
INSERT INTO pgconfig_target (schema_name, target_name, target_path)
VALUES (s_name, t_name, t_path)
ON CONFLICT DO NOTHING;
-- The ID should be there, either historically or becase it was just added.
SELECT id
FROM pgconfig_target
WHERE schema_name = s_name
AND target_name = t_name
AND target_path = t_path
INTO id_out;
return id_out;
END
$BODY$
LANGUAGE plpgsql;
这样的示例调用:
select * from pgconfig_target_add_if_missing ('hello', 'world', 'checking.it.out')
我正在使用 select *
,因为我没有理清使 id
成为明确结果的语法。
最佳答案
这就是 on conflict
的工作原理。如果没有插入行,则不返回任何内容。
解决方法是使用 CTE 重新表述逻辑:首先插入(或什么都不做),然后再次从表中选择,使用输入数据进行过滤。
with
data as (
select *
from (values
('hello','world','passthrough.hello.world')
) v(schema_name, target_name, target_path)
),
ins as (
insert into pgconfig_target (schema_name, target_name, target_path)
select *
from data
on conflict (schema_name, target_name, target_path) do nothing
)
select c.id
from pgconfig_target c
inner join data d using (schema_name, target_name, target_path)
关于sql - 插入和返回新记录或现有记录的 ID,在 Postgres 12.5 中需要更好的策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64920254/