我遇到了一个问题,我的主键序列与我的表行不同步。
也就是说,当我插入一个新行时,我得到一个重复的键错误,因为在串行数据类型中隐含的序列返回一个已经存在的数字。
这似乎是由于导入/恢复没有正确地维护序列造成的。
我遇到了一个问题,我的主键序列与我的表行不同步。
也就是说,当我插入一个新行时,我得到一个重复的键错误,因为在串行数据类型中隐含的序列返回一个已经存在的数字。
这似乎是由于导入/恢复没有正确地维护序列造成的。
当前回答
ALTER SEQUENCE sequence_name RESTART WITH (SELECT max(id) FROM table_name); 是行不通的。
复制自@tardate答案:
SELECT setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM table_name;
其他回答
Pg_get_serial_sequence可以用来避免任何关于序列名称的错误假设。这将在一个镜头中重置序列:
SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), (SELECT MAX(id) FROM table_name)+1);
或者更简洁地说:
SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM table_name;
然而,这个表单不能正确地处理空表,因为max(id)是空的,你也不能设置val 0,因为它超出了序列的范围。一种解决方法是使用ALTER SEQUENCE语法。
ALTER SEQUENCE table_name_id_seq RESTART WITH 1;
ALTER SEQUENCE table_name_id_seq RESTART; -- 8.4 or higher
但是ALTER SEQUENCE的用途有限,因为序列名和重启值不能是表达式。
似乎最好的通用解决方案是调用setval,将false作为第三个参数,允许我们指定“下一个要使用的值”:
SELECT setval(pg_get_serial_sequence('t1', 'id'), coalesce(max(id),0) + 1, false) FROM t1;
这符合我所有的条件:
避免硬编码实际的序列名 正确处理空表 使用现有数据处理表,并且不会留下 序列中的漏洞
最后,请注意,pg_get_serial_sequence仅在序列属于列时才有效。如果将递增列定义为串行类型,则会出现这种情况,但是如果手动添加序列,则需要确保ALTER sequence。“OWNED BY”也被执行。
例如,如果使用serial type来创建表,这应该都可以工作:
CREATE TABLE t1 (
id serial,
name varchar(20)
);
SELECT pg_get_serial_sequence('t1', 'id'); -- returns 't1_id_seq'
-- reset the sequence, regardless whether table has rows or not:
SELECT setval(pg_get_serial_sequence('t1', 'id'), coalesce(max(id),0) + 1, false) FROM t1;
但如果手动添加序列:
CREATE TABLE t2 (
id integer NOT NULL,
name varchar(20)
);
CREATE SEQUENCE t2_custom_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE t2 ALTER COLUMN id SET DEFAULT nextval('t2_custom_id_seq'::regclass);
ALTER SEQUENCE t2_custom_id_seq OWNED BY t2.id; -- required for pg_get_serial_sequence
SELECT pg_get_serial_sequence('t2', 'id'); -- returns 't2_custom_id_seq'
-- reset the sequence, regardless whether table has rows or not:
SELECT setval(pg_get_serial_sequence('t2', 'id'), coalesce(max(id),0) + 1, false) FROM t1;
select 'SELECT SETVAL(' || seq [ 1] || ', COALESCE(MAX('||column_name||')+1, 1) ) FROM '||table_name||';'
from (
SELECT table_name, column_name, column_default, regexp_match(column_default, '''.*''') as seq
from information_schema.columns
where column_default ilike 'nextval%'
) as sequense_query
这里有一些非常硬核的答案,我假设它在被问到这个问题的时候是非常糟糕的,因为这里的很多答案在9.3版本中都不适用。8.0版本以后的文档提供了这个问题的答案:
SELECT setval('serial', max(id)) FROM distributors;
另外,如果你需要注意区分大小写的序列名,你可以这样做:
SELECT setval('"Serial"', max(id)) FROM distributors;
-- Login to psql and run the following
-- What is the result?
SELECT MAX(id) FROM your_table;
-- Then run...
-- This should be higher than the last result.
SELECT nextval('your_table_id_seq');
-- If it's not higher... run this set the sequence last to your highest id.
-- (wise to run a quick pg_dump first...)
BEGIN;
-- protect against concurrent inserts while you update the counter
LOCK TABLE your_table IN EXCLUSIVE MODE;
-- Update the sequence
SELECT setval('your_table_id_seq', COALESCE((SELECT MAX(id)+1 FROM your_table), 1), false);
COMMIT;
来源- Ruby论坛
最短最快的路
SELECT setval('tbl_tbl_id_seq', max(tbl_id)) FROM tbl;
tbl_id是表tbl的串行列或IDENTITY列,取自序列tbl_tbl_id_seq(生成的默认名称)。看到的:
自动递增表列
如果你不知道附加序列的名称(不一定是默认形式),使用pg_get_serial_sequence()(也适用于IDENTITY):
SELECT setval(pg_get_serial_sequence('tbl', 'tbl_id'), max(tbl_id)) FROM tbl;
这里没有差1的误差。手册:
双参数形式将序列的last_value字段设置为 值,并将其is_called字段设置为true Next nextval将在返回值之前推进序列。
大胆强调我的。
如果表可以为空,在这种情况下实际从1开始:
SELECT setval(pg_get_serial_sequence('tbl', 'tbl_id')
, COALESCE(max(tbl_id) + 1, 1)
, false)
FROM tbl;
我们不能只使用2参数形式并从0开始,因为序列的下界默认为1(除非自定义)。
在并发写负载下安全
为了防止并发序列活动或写入,可以在SHARE模式下锁定表。它可以防止并发事务写入更高的数字(或任何东西)。
还要考虑到客户端可能在主表上没有任何锁的情况下提前获取了序列号,然而(可能发生在某些设置中),只增加序列的当前值,而不要减少它。这可能看起来有些偏执,但这符合序列的本质,并可以防止并发性问题。
BEGIN;
LOCK TABLE tbl IN SHARE MODE;
SELECT setval('tbl_tbl_id_seq', max(tbl_id))
FROM tbl
HAVING max(tbl_id) > (SELECT last_value FROM tbl_tbl_id_seq); -- prevent lower number
COMMIT;
SHARE模式足够强大。手册:
这种模式保护表不受并发数据更改的影响。
与ROW EXCLUSIVE模式冲突。
命令UPDATE、DELETE和INSERT在目标表上获得这种锁定模式。