我在生命中第一次将MySQL表转换为PostgreSQL,并遇到了没有auto_increment的传统新手问题。
现在我发现postgres解决方案是使用一个序列,然后在每次插入时请求此序列的nextval()作为默认值。我还读过SERIAL类型自动创建序列和主键,即使在事务内部调用时,nextval()也会递增计数器,以避免锁定序列。
我无法解决的问题是当您将值手动插入具有UNIQUE或PRIMARY约束以及序列的nextval()作为默认值的字段时会发生什么。据我所知,当序列达到该值时,这会导致INSERT失败。
是否有一种简单(或常见)的方法来解决这个问题?
非常感谢明确的解释。
更新:如果您认为我不应该这样做,将永远无法解决这个问题或者做出一些有缺陷的假设,请随时在答案中指出它们。最重要的是,请告诉我该怎么做,为程序员提供一个稳定而强大的数据库,不能通过简单的插入来破坏(最好不要隐藏存储过程背后的所有内容)
答案 0 :(得分:10)
如果您正在迁移数据,那么我会删除列上的序列约束,执行所有插入操作,使用setval()将序列设置为数据的最大值,然后恢复列序列nextval()默认。
答案 1 :(得分:4)
为了扩展Tometzky的优秀答案,这是一个更通用的版本:
CREATE OR REPLACE FUNCTION check_serial() RETURNS trigger AS $$
BEGIN
IF currval(TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME || '_' || TG_ARGV[0] || '_seq') <
(row_to_json(NEW)->>TG_ARGV[0])::bigint
THEN RAISE SQLSTATE '55000'; -- same as currval() of uninitialized sequence
END IF;
RETURN NULL;
EXCEPTION
WHEN SQLSTATE '55000'
THEN RAISE 'manual entry of serial field %.%.% disallowed',
TG_TABLE_SCHEMA, TG_TABLE_NAME, TG_ARGV[0]
USING HINT = 'use DEFAULT instead of specifying value manually',
SCHEMA = TG_TABLE_SCHEMA, TABLE = TG_TABLE_NAME, COLUMN = TG_ARGV[0];
END;
$$ LANGUAGE plpgsql;
您可以将其应用于任何列,例如test.id,因此:
CREATE CONSTRAINT TRIGGER test_id_check
AFTER INSERT OR UPDATE OF id ON test
FOR EACH ROW EXECUTE PROCEDURE check_serial(id);
答案 2 :(得分:3)
您可以创建一个触发器来检查currval('id_sequence_name')>=NEW.id
。
如果您的交易未使用默认值或nextval('id_sequence_name')
,则currval
函数将抛出错误,因为它仅在当前会话中更新序列时才起作用。如果您使用nextval
然后尝试插入更大的主键,则会引发另一个错误。然后中止交易。
这样可以防止插入任何会破坏串行的坏主键。
示例代码:
create table test (id serial primary key, value text);
create or replace function test_id_check() returns trigger language plpgsql as
$$ begin
if ( currval('test_id_seq')<NEW.id ) then
raise exception 'currval(test_id_seq)<id';
end if;
return NEW;
end; $$;
create trigger test_id_seq_check before insert or update of id on test
for each row execute procedure test_id_check();
然后使用默认主键插入将正常工作:
insert into test(value) values ('a'),('b'),('c'),('d');
但插入太大的主键会出错并中止:
insert into test(id, value) values (10,'z');
答案 3 :(得分:2)
我不完全理解你的问题,但是如果你的目标只是做插入,并且有一个有效的字段(例如一个id),那么插入没有id字段的值,这就是“默认”代表的意思。它会起作用。
E.g。如果表定义中id serial NOT NULL
和CONSTRAINT table_pkey PRIMARY KEY(id)
将自动设置ID并自动增加序列table_id_seq
。
答案 4 :(得分:1)
使用CHECK怎么样?
<p><?php printf( __( 'For Seller resources, click <a href="%s">here</a>.', 'woocommerce' ), 'http://yoururlhere.com' ); ?></p