我正在使用postgres 9.4;我有一个具有唯一索引的表。我想通过添加后缀来改变名称,以确保名称是唯一的。
我创建了一个“before”触发器来计算后缀。它在自动提交模式下运行良好。但是,如果在同一事务中插入了两个具有相同名称的项目,则它们都会获得相同的唯一后缀。
完成任务的最佳方法是什么?有没有办法用触发器处理它,或者我应该...嗯...将插入或更新包装在保存点然后处理错误?
更新(来自@Haleemur Ali的评论):
我不认为我的问题取决于细节。重点是我查询了我想要强制执行唯一性的集合的子集,以及 选择一个新名称...但是,当在同一个事务中同名的两个对象上运行查询时,似乎没有看到其他人对新值的修改。
但是......以防万一...我的触发器包含(“type”是触发器功能的固定参数):
select find_unique(coalesce(new.name, capitalize(type)),
'vis_operation', 'name', format(
'sheet_id = %s', new.sheet_id )) into new.name;
“find_unique”包含:
create or replace function find_unique(
stem text, table_name text, column_name text, where_expr text = null)
returns text language plpgsql as $$
declare
table_nt text = quote_ident(table_name);
column_nt text = quote_ident(column_name);
bstem text = replace(btrim(stem),'''', '''''');
find_re text = quote_literal(format('^%s(( \d+$)|$)', bstem));
xtct_re text = quote_literal(format('^(%s ?)', bstem));
where_ext text = case when where_expr is null then '' else 'and ' || where_expr end;
query_exists text = format(
$Q$ select 1 from %1$s where btrim(%2$s) = %3$s %4$s $Q$,
table_nt, column_nt, quote_literal(bstem), where_ext );
query_max text = format($q$
select max(coalesce(nullif(regexp_replace(%1$s, %4$s, '', 'i'), ''), '0')::int)
from %2$s where %1$s ~* %3$s %5$s
$q$,
column_nt, table_nt, find_re, xtct_re, where_ext );
last int;
i int;
begin
-- if no exact match, use exact
execute query_exists;
get diagnostics i = row_count;
if i = 0 then
return coalesce(bstem, capitalize(right(table_nt,4)));
end if;
-- find stem w/ number, use max plus one.
execute query_max into last;
if last is null then
return coalesce(bstem, capitalize(right(table_nt,4)));
end if;
return format('%s %s', bstem, last + 1);
end;
$$;
答案 0 :(得分:1)
BEFORE
触发器会看到由当前正在运行的语句修改的行。所以这应该工作。见下面的演示。
但是,您的设计不在并发存在的情况下工作。您必须LOCK TABLE ... IN EXCLUSIVE MODE
要更新的表,否则并发事务可能会获得相同的后缀。或者,如果存在UNIQUE
约束,则除了一个之外的所有约束都会出错。
我个人建议:
EXCLUSIVE
模式锁定边桌。这将序列化所有创建条目的会话,这是必要的,以便您可以:UPDATE side_table SET counter = counter + 1 WHERE name = $1 RETURNING counter
获取下一个免费ID。如果你获得零行,那么:显示BEFORE
个触发器的演示可以看到在同一个语句中插入的行,但不是触发触发器的行:
craig=> CREATE TABLE demo(id integer);
CREATE TABLE
craig=> \e
CREATE FUNCTION
craig=> CREATE OR REPLACE FUNCTION demo_tg() RETURNS trigger LANGUAGE plpgsql AS $$
DECLARE
row record;
BEGIN
FOR row IN SELECT * FROM demo
LOOP
RAISE NOTICE 'Row is %',row;
END LOOP;
IF tg_op = 'DELETE' THEN
RETURN OLD;
ELSE
RETURN NEW;
END IF;
END;
$$;
CREATE FUNCTION
craig=> CREATE TRIGGER demo_tg BEFORE INSERT OR UPDATE OR DELETE ON demo FOR EACH ROW EXECUTE PROCEDURE demo_tg();
CREATE TRIGGER
craig=> INSERT INTO demo(id) VALUES (1),(2);
NOTICE: Row is (1)
INSERT 0 2
craig=> INSERT INTO demo(id) VALUES (3),(4);
NOTICE: Row is (1)
NOTICE: Row is (2)
NOTICE: Row is (1)
NOTICE: Row is (2)
NOTICE: Row is (3)
INSERT 0 2
craig=> UPDATE demo SET id = id + 100;
NOTICE: Row is (1)
NOTICE: Row is (2)
NOTICE: Row is (3)
NOTICE: Row is (4)
NOTICE: Row is (2)
NOTICE: Row is (3)
NOTICE: Row is (4)
NOTICE: Row is (101)
NOTICE: Row is (3)
NOTICE: Row is (4)
NOTICE: Row is (101)
NOTICE: Row is (102)
NOTICE: Row is (4)
NOTICE: Row is (101)
NOTICE: Row is (102)
NOTICE: Row is (103)
UPDATE 4
craig=>