How to reset postgres' primary key sequence when it falls out of sync?
我遇到了我的主键序列与我的表行不同步的问题。
也就是说,当我插入一个新行时,我得到一个重复的键错误,因为串行数据类型中隐含的序列返回一个已经存在的数字。
这似乎是由导入/恢复不能正确维护序列引起的。
- 我很好奇..你在恢复之前丢弃数据库了吗? 我对这种情况有一种微弱的回忆,但我可能错了:P
- PostgreSQL wiki有一个关于Fixing Sequences的页面。
- 只是为了帮助googleability,这里抛出的错误信息是:"重复键值违反了唯一约束......"
-
这就是Django中sqlsequencereset的用法:SELECT setval(pg_get_serial_sequence("
",'id'),coalesce(max("id"),1),max("id")IS not null)FROM"<表名>"; -
的第一个实例需要用单引号括起来使pg_get_serioal_sequence函数起作用:SELECT setval(pg_get_serial_sequence('
','id'),coalesce(max("id"),1) ,max("id")IS not null)FROM" "
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18-- 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论坛
- 谢谢,phpPgAdmin有一个名为'Indexes'的选项卡,所以我认为这是正确的命名。所以我猜'sequence'是'index'的属性,它会被更新?
- 序列只是一个特殊值,可以使用nextval()和setval()函数进行递增/设置。它主要用于创建将自动递增的列,您有一个主键,其默认值为'nextval('primary_key_name_seq'),因此每次插入时...
- ...一个新行,该值从序列中获得。索引是一个完全不同的野兽,它们也会自动为主键创建,但与下一个值无关。我好久没见过phpPgAdmin了,但我倾向于认为那个标签显示了......
- 例如,真实的索引以及序列。
- 无论如何,将MAX(id)加1会在ID中留下一个数字间隙,因为setval集是序列的最后一个值,而不是下一个值。
- 如果表中没有行,则示例将不起作用。所以下面给出的SQL更安全:SELECT setval('your_table_id_seq',coalesce((从your_table中选择max(id)+1),1),true);
-
@Valery:但是为了避免上面提到的两条评论@mikl提到的空白,你需要
SELECT setval('your_table_id_seq', coalesce((select max(id)+1 from your_table), 1), false); - 不起作用:(
- 差距实际上有什么不同吗?
-
所有问题都已解决并合并为一个查询:
SELECT setval('your_seq',(SELECT GREATEST(MAX(your_id)+1,nextval('your_seq'))-1 FROM your_table)) - @Frunsi你的查询对我不起作用,因为nextval已经太大了。 Antony Hatchkins发布的那个对我来说效果更好
- 你不知道这对我有多大帮助!神的圣洁母亲 !
- 如果您的应用程序关注序列中的间隙,那么您的应用程序就会中断。序列中的间隙是正常的,并且可能由于计划外数据库关闭,错误后的事务回滚等原因而发生。
-
@Frunsi提交的查询可以进行一些调整:
SELECT setval(pg_get_serial_sequence('table_name', 'id'), COALESCE(MAX(id), 1), MAX(id) IS NOT null) FROM table_name (注意使用引号) - 有人可以提供一些关于如何在Postgres获得"your_table_id_seq"的颜色吗?
-
跟进如何获取表的序列名称:postgresql.org/message-id/[email protected]
SELECT table_name, column_name, column_default from information_schema.columns where table_name='your_table'; - 救了我的一天。谢谢。
-
@mikl它取决于boolean参数(deafults to
true )。如果false 则设置下一个值,如果true 则设置当前值。
pg_get_serial_sequence 可用于避免对序列名称的任何不正确的假设。这会一次性重置序列:1SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), (SELECT MAX(id) FROM TABLE_NAME)+1);或者更简洁:
1SELECT pg_catalog.setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM TABLE_NAME;但是这个表单无法正确处理空表,因为max(id)为null,并且你也不能setval 0因为它超出了序列的范围。一种解决方法是采用
ALTER SEQUENCE 语法,即1
2ALTER SEQUENCE table_name_id_seq RESTART WITH 1;
ALTER SEQUENCE table_name_id_seq RESTART; -- 8.4 or higher但
ALTER SEQUENCE 的用途有限,因为序列名称和重启值不能是表达式。似乎最好的通用解决方案是使用false作为第三个参数调用
setval ,允许我们指定"下一个要使用的值":1SELECT SETVAL(pg_get_serial_sequence('t1', 'id'), COALESCE(MAX(id),0) + 1, FALSE) FROM t1;这勾选了我的所有方框:
- 避免硬编码实际的序列名称
- 正确处理空表
- 处理具有现有数据的表,并且不会留下
序列中的洞最后,请注意
pg_get_serial_sequence 仅在序列归列所有时才有效。如果将递增列定义为serial 类型,则会出现这种情况,但是如果手动添加序列,则必须确保也执行ALTER SEQUENCE .. OWNED BY 。即如果
serial 类型用于表创建,这应该都有效:1
2
3
4
5
6
7
8
9CREATE 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;但是如果手动添加序列:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20CREATE 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;-
查询中不需要'+1',
setval() 设置当前值,nextval() 将返回当前值+1。 - 包含此方法的函数接受一个参数 - table_name - 在我的答案中如下:stackoverflow.com/a/13308052/237105
- @AntonyHatchkins欢呼。刚刚看到+1错误的另一个重复,所以最后拍了好,我希望
最短最快的方式:
1SELECT SETVAL('tbl_tbl_id_seq', MAX(tbl_id)) FROM tbl;tbl_id 是表tbl 的serial 列,从序列tbl_tbl_id_seq (这是默认的自动名称)中提取。如果您不知道附加序列的名称(不必是默认格式),请使用
pg_get_serial_sequence() :1SELECT SETVAL(pg_get_serial_sequence('tbl', 'tbl_id'), MAX(tbl_id)) FROM tbl;这里没有一个错误的错误。每个文件:
The two-parameter form sets the sequence's last_value field to the
specified value and sets itsis_called field to true, meaning that the
nextnextval will advance the sequence before returning a value.大胆强调我的。
并发
但是,在上述查询中,并没有防止并发序列活动或写入表。如果这是相关的,您可以将表锁定为独占模式。当您尝试同步时,它可以防止并发事务写入更高的数字。 (它还会暂时阻止无害的写入而不会弄乱最大数量。)
但它没有考虑到客户端可能提前获取序列号而没有主表上的任何锁定(但可能发生)。为了实现这一点,也只增加序列的当前值,而不是减少它。它可能看起来很偏执,但这符合序列的本质和防御并发问题。
1
2
3
4
5
6
7
8
9BEGIN;
LOCK TABLE tbl IN EXCLUSIVE MODE;
SELECT SETVAL('tbl_tbl_id_seq', MAX(tbl_id))
FROM tbl
HAVING MAX(tbl_id) > (SELECT last_value FROM tbl_tbl_id_seq);
COMMIT;- 第二个查询对我来说就像一个魅力!
-
哪里有"标准社区 - 基本功能库"?
EXECUTE format() (如@ EB。)中这个答案的第二个选择子句是一个基本函数!如何解决PostgreSQL中缺少标准库的问题???? - 没有关系,如果有一个一个一个。序列中的间隙是正常的。如果您的应用无法应对,您的应用就会中断,因为事务回滚,计划外服务器关闭等也会出现差距。
- @Craig:我解决的一个错误(并且不存在)很重要,因为否则会冒重复键错误。你考虑的方向相反;似乎是一种误解。
- 啊,有道理。
这将重置所有公共序列,不对表名或列名进行假设。在8.4版本上测试过
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16CREATE OR REPLACE FUNCTION"reset_sequence" (tablename text, columnname text, sequence_name text) RETURNS"pg_catalog"."void" AS
$body$
DECLARE
BEGIN
EXECUTE 'SELECT setval( ''' || sequence_name || ''', ' || '(SELECT MAX(' || columnname || ') FROM ' || tablename || ')' || '+1)';
END;
$body$ LANGUAGE 'plpgsql';
SELECT TABLE_NAME || '_' || column_name || '_seq', reset_sequence(TABLE_NAME, column_name, TABLE_NAME || '_' || column_name || '_seq') FROM information_schema.columns WHERE column_default LIKE 'nextval%';-
+1非常有用的功能!我们的序列名称与表名称不完全匹配,因此我使用
substring(column_default, '''(.*)''') 而不是table_name || '_' || column_name || '_seq' 。完美的工作。 - +1正是我想要的。谢谢。
-
请注意,这将失败,序列名称包含单引号,或者名称中包含大写字母,空格等的表名称。应该真正使用
quote_literal 和quote_ident 函数,或者最好是format 函数。 - 希望我能给这个不止一票......很好的先生。至少对我来说,在Postgres 9.1上工作得很好。
-
这很棒。我使用
substring(column_default from 'nextval\(''(.+)''::regclass\)') 来显式获取序列名称。工作就像一个魅力。
ALTER SEQUENCE sequence_name RESTART WITH(SELECT max(id)FROM table_name); strike>
不行。复制自@tardate回答:
1SELECT SETVAL(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM TABLE_NAME;- 这是我在8.4中的语法错误(在^(SELECT ...)。RESTART WITH似乎只接受一个序数值。这有效:SELECT setval(pg_get_serial_sequence('table_name','id'),(SELECT MAX( id)FROM table_name)+ 1);
- @tardate,完美,谢谢!
- Muruges的解决方案在9.4中也不起作用。不明白为什么这么多赞成这个答案。 ALTER SEQUENCE不允许子查询。 @tardate的解决方案非常有效。编辑回答删除不正确的数据。
- ALTER SEQUENCE对我来说非常适合。我使用COPY来引入一些数据,主键中存在间隙,INSERT正在抛出重复的键异常。设置序列就可以了。 9.4
此命令仅用于更改postgresql中自动生成的键序列值
1ALTER SEQUENCE"your_sequence_name" RESTART WITH 0;代替零,您可以放置??要重新启动序列的任何数字。
默认序列名称将
"TableName_FieldName_seq" 。例如,如果表名为"MyTable" 且字段名称为"MyID" ,则序列名称将为"MyTable_MyID_seq" 。答案与@ murugesanponappan的答案相同,但他的解决方案中存在语法错误。您不能在
alter 命令中使用子查询(select max()...) 。因此,您必须使用固定数值,或者需要使用变量代替子查询。- 这是完美的解决方案非常感谢先生。但在我的情况下,我有一个错误,所以我不得不将其更改为ALTER SEQUENCE"your_sequence_name"RESTART WITH 1;
重置所有序列,没有关于名称的假设,除了每个表的主键是"id":
1
2
3
4
5
6
7
8
9
10
11CREATE OR REPLACE FUNCTION"reset_sequence" (tablename text, columnname text)
RETURNS"pg_catalog"."void" AS
$body$
DECLARE
BEGIN
EXECUTE 'SELECT setval( pg_get_serial_sequence(''' || tablename || ''', ''' || columnname || '''),
(SELECT COALESCE(MAX(id)+1,1) FROM ' || tablename || '), false)';
END;
$body$ LANGUAGE 'plpgsql';
SELECT TABLE_NAME || '_' || column_name || '_seq', reset_sequence(TABLE_NAME, column_name) FROM information_schema.columns WHERE column_default LIKE 'nextval%';- 完美地使用了我的9.1版本
-
如果表包含大写,则需要添加引号:
pg_get_serial_sequence(''"' || tablename || '"'' -
这是最好的功能!您可以使用格式(例如
EXECUTE format( 'SELECT setval(pg_get_serial_sequence(%L, %L), coalesce(max(id),0) + 1, false) FROM %I;', $1,$2,$1 ); )来避免引用问题(并增强优雅)
当序列名,列名,表名或模式名具有空格,标点符号等有趣字符时,这些函数充满了危险。我写了这个:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27CREATE OR REPLACE FUNCTION sequence_max_value(oid) RETURNS BIGINT
VOLATILE STRICT LANGUAGE plpgsql AS $$
DECLARE
tabrelid oid;
colname name;
r record;
newmax BIGINT;
BEGIN
FOR tabrelid, colname IN SELECT attrelid, attname
FROM pg_attribute
WHERE (attrelid, attnum) IN (
SELECT adrelid::regclass,adnum
FROM pg_attrdef
WHERE oid IN (SELECT objid
FROM pg_depend
WHERE refobjid = $1
AND classid = 'pg_attrdef'::regclass
)
) LOOP
FOR r IN EXECUTE 'SELECT max(' || quote_ident(colname) || ') FROM ' || tabrelid::regclass LOOP
IF newmax IS NULL OR r.max > newmax THEN
newmax := r.max;
END IF;
END LOOP;
END LOOP;
RETURN newmax;
END; $$ ;您可以通过向OID传递它来为单个序列调用它,它将返回任何具有默认序列的表所使用的最大数字;或者您可以使用这样的查询运行它,以重置数据库中的所有序列:
1
2
3SELECT relname, SETVAL(oid, sequence_max_value(oid))
FROM pg_class
WHERE relkind = 'S';使用不同的qual可以仅重置某个模式中的序列,依此类推。例如,如果要调整"公共"模式中的序列:
1
2
3
4
5SELECT relname, SETVAL(pg_class.oid, sequence_max_value(pg_class.oid))
FROM pg_class, pg_namespace
WHERE pg_class.relnamespace = pg_namespace.oid AND
nspname = 'public' AND
relkind = 'S';请注意,由于setval()的工作原理,您无需在结果中添加1。
作为结束语,我必须警告一些数据库似乎有默认链接到序列的方式不会让系统目录具有它们的完整信息。当你在psql的 d中看到这样的事情时会发生这种情况:
1
2
3
4
5alvherre=# \d baz
Tabla ?public.baz?
Columna | Tipo | Modificadores
---------+---------+------------------------------------------------
a | INTEGER | DEFAULT NEXTVAL(('foo_a_seq'::text)::regclass)请注意,除了:: regclass强制转换之外,该default子句中的nextval()调用还有一个:: text强制转换。我认为这是因为数据库是旧的PostgreSQL版本的pg_dump。会发生的是上面的函数sequence_max_value()将忽略这样的表。要解决此问题,您可以重新定义DEFAULT子句以直接引用序列而不使用强制转换:
1
2alvherre=# ALTER TABLE baz ALTER a SET DEFAULT NEXTVAL('foo_a_seq');
ALTER TABLE然后psql正确显示它:
1
2
3
4
5alvherre=# \d baz
Tabla ?public.baz?
Columna | Tipo | Modificadores
---------+---------+----------------------------------------
a | INTEGER | DEFAULT NEXTVAL('foo_a_seq'::regclass)一旦修复了该函数,该函数就可以正常运行此表以及可能使用相同序列的所有其他函数。
-
这太棒了!应该注意的是,我需要在赋值中添加一个强制转换(功能代码中的第21行),如下所示:
newmax := r.max::bigint; 使其适合我。 -
不得不改变它:
'SELECT max(' || quote_ident(colname) || ') FROM ' =>'SELECT max(' || quote_ident(colname) || '::bigint) FROM ' 注意动态构建查询中添加的::bigint 强制转换。
从公共重置所有序列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15CREATE OR REPLACE FUNCTION"reset_sequence" (tablename text) RETURNS"pg_catalog"."void" AS
$body$
DECLARE
BEGIN
EXECUTE 'SELECT setval( '''
|| tablename
|| '_id_seq'', '
|| '(SELECT id + 1 FROM"'
|| tablename
|| '" ORDER BY id DESC LIMIT 1), false)';
END;
$body$ LANGUAGE 'plpgsql';
SELECT sequence_name, reset_sequence(split_part(sequence_name, '_id_seq',1)) FROM information_schema.sequences
WHERE sequence_schema='public';- 似乎这种方法对列和表名称进行了假设,因此它对我不起作用
- 这不会损坏数据库中的数据吗?
我的版本使用第一个,有一些错误检查...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26BEGIN;
CREATE OR REPLACE FUNCTION reset_sequence(_table_schema text, _tablename text, _columnname text, _sequence_name text)
RETURNS pg_catalog.void AS
$BODY$
DECLARE
BEGIN
PERFORM 1
FROM information_schema.sequences
WHERE
sequence_schema = _table_schema AND
sequence_name = _sequence_name;
IF FOUND THEN
EXECUTE 'SELECT setval( ''' || _table_schema || '.' || _sequence_name || ''', ' || '(SELECT MAX(' || _columnname || ') FROM ' || _table_schema || '.' || _tablename || ')' || '+1)';
ELSE
RAISE WARNING 'SEQUENCE NOT UPDATED ON %.%', _tablename, _columnname;
END IF;
END;
$BODY$
LANGUAGE 'plpgsql';
SELECT reset_sequence(table_schema, TABLE_NAME, column_name, TABLE_NAME || '_' || column_name || '_seq')
FROM information_schema.columns
WHERE column_default LIKE 'nextval%';
DROP FUNCTION reset_sequence(_table_schema text, _tablename text, _columnname text, _sequence_name text) ;
COMMIT;-
谢谢你的错误检查!非常感谢,因为如果表格/列名称太长,会被截断,而
RAISE WARNING 为我
这里有一些非常硬核的答案,我假设在这个问题的时候它曾经非常糟糕,因为这里的很多答案都不适用于9.3版本。自8.0版以来的文档提供了这个问题的答案:
1SELECT SETVAL('serial', MAX(id)) FROM distributors;此外,如果您需要处理区分大小写的序列名称,那么您就是这样做的:
1SELECT SETVAL('"Serial"', MAX(id)) FROM distributors;- 非常简洁准确的解决方案。谢谢
我建议在postgres wiki上找到这个解决方案。它会更新表的所有序列。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16SELECT 'SELECT SETVAL(' ||
quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';'
FROM pg_class AS S,
pg_depend AS D,
pg_class AS T,
pg_attribute AS C,
pg_tables AS PGT
WHERE S.relkind = 'S'
AND S.oid = D.objid
AND D.refobjid = T.oid
AND D.refobjid = C.attrelid
AND D.refobjsubid = C.attnum
AND T.relname = PGT.tablename
ORDER BY S.relname;如何使用(来自postgres wiki):
- 将其保存到文件中,例如'reset.sql'
- 运行该文件并以不包含常用标题的方式保存其输出,然后运行该输出。例:
例:
1
2
3psql -Atq -f reset.sql -o temp
psql -f temp
rm temp原始文章(也有序列所有权的修复)在这里
另一个plpgsql - 仅在
max(att) > then lastval 时重置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26do --check seq not in sync
$$
DECLARE
_r record;
_i BIGINT;
_m BIGINT;
BEGIN
FOR _r IN (
SELECT relname,nspname,d.refobjid::regclass, a.attname, refobjid
FROM pg_depend d
JOIN pg_attribute a ON a.attrelid = d.refobjid AND a.attnum = d.refobjsubid
JOIN pg_class r ON r.oid = objid
JOIN pg_namespace n ON n.oid = relnamespace
WHERE d.refobjsubid > 0 AND relkind = 'S'
) loop
EXECUTE format('select last_value from %I.%I',_r.nspname,_r.relname) INTO _i;
EXECUTE format('select max(%I) from %s',_r.attname,_r.refobjid) INTO _m;
IF COALESCE(_m,0) > _i THEN
raise info '%',concat('changed: ',_r.nspname,'.',_r.relname,' from:',_i,' to:',_m);
EXECUTE format('alter sequence %I.%I restart with %s',_r.nspname,_r.relname,_m+1);
END IF;
END loop;
END;
$$
;同时注释行
--execute format('alter sequence 将给出列表,而不是实际重置值
当使用实体框架创建数据库然后使用初始数据为数据库播种时,这个问题发生在我身上,这使得序列不匹配。
我通过创建一个脚本来播种数据库后解决它:
1
2
3
4
5
6
7
8
9
10
11DO
$do$
DECLARE tablename text;
BEGIN
-- change the where statments to include or exclude whatever tables you need
FOR tablename IN SELECT TABLE_NAME FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE' AND TABLE_NAME != '__EFMigrationsHistory'
LOOP
EXECUTE format('SELECT setval(pg_get_serial_sequence(''"%s"'', ''Id''), (SELECT MAX("Id") + 1 from"%s"))', tablename, tablename);
END LOOP;
END
$do$-
为什么
MAX("Id") + 1 当序列=最大时,它最适合我。
把它们放在一起
1
2
3
4
5
6
7
8
9CREATE OR REPLACE FUNCTION"reset_sequence" (tablename text)
RETURNS"pg_catalog"."void" AS
$body$
DECLARE
BEGIN
EXECUTE 'SELECT setval( pg_get_serial_sequence(''' || tablename || ''', ''id''),
(SELECT COALESCE(MAX(id)+1,1) FROM ' || tablename || '), false)';
END;
$body$ LANGUAGE 'plpgsql';将修复给定表的'
id' 序列(例如,通常需要使用django)。
在我还没有尝试过代码之前:在下面我发帖
Klaus和user457226解决方案的sql-code版本
在我的电脑上工作[Postgres 8.3],只有一些小调整
对于Klaus one和我的版本为user457226一个。克劳斯解决方案:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26DROP FUNCTION IF EXISTS rebuilt_sequences() RESTRICT;
CREATE OR REPLACE FUNCTION rebuilt_sequences() RETURNS INTEGER AS
$body$
DECLARE sequencedefs RECORD; c INTEGER ;
BEGIN
FOR sequencedefs IN SELECT
constraint_column_usage.table_name AS tablename,
constraint_column_usage.table_name AS tablename,
constraint_column_usage.column_name AS columnname,
REPLACE(REPLACE(COLUMNS.column_default,'''::regclass)',''),'nextval(''','') AS sequencename
FROM information_schema.constraint_column_usage, information_schema.columns
WHERE constraint_column_usage.table_schema ='public' AND
COLUMNS.table_schema = 'public' AND COLUMNS.table_name=constraint_column_usage.table_name
AND constraint_column_usage.column_name = COLUMNS.column_name
AND COLUMNS.column_default IS NOT NULL
LOOP
EXECUTE 'select max('||sequencedefs.columnname||') from ' || sequencedefs.tablename INTO c;
IF c IS NULL THEN c = 0; END IF;
IF c IS NOT NULL THEN c = c+ 1; END IF;
EXECUTE 'alter sequence ' || sequencedefs.sequencename ||' restart with ' || c;
END LOOP;
RETURN 1; END;
$body$ LANGUAGE plpgsql;
SELECT rebuilt_sequences();user457226解决方案:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24--drop function IF EXISTS reset_sequence (text,text) RESTRICT;
CREATE OR REPLACE FUNCTION"reset_sequence" (tablename text,columnname text) RETURNS BIGINT --"pg_catalog"."void"
AS
$body$
DECLARE seqname CHARACTER VARYING;
c INTEGER;
BEGIN
SELECT tablename || '_' || columnname || '_seq' INTO seqname;
EXECUTE 'SELECT max("' || columnname || '") FROM"' || tablename || '"' INTO c;
IF c IS NULL THEN c = 0; END IF;
c = c+1; --because of substitution of setval with"alter sequence"
--EXECUTE 'SELECT setval("' || seqname || '", ' || cast(c as character varying) || ', false)'; DOES NOT WORK!!!
EXECUTE 'alter sequence ' || seqname ||' restart with ' || CAST(c AS CHARACTER VARYING);
RETURN NEXTVAL(seqname)-1;
END;
$body$ LANGUAGE 'plpgsql';
SELECT sequence_name, PG_CLASS.relname, PG_ATTRIBUTE.attname,
reset_sequence(PG_CLASS.relname,PG_ATTRIBUTE.attname)
FROM PG_CLASS
JOIN PG_ATTRIBUTE ON PG_ATTRIBUTE.attrelid = PG_CLASS.oid
JOIN information_schema.sequences
ON information_schema.sequences.sequence_name = PG_CLASS.relname || '_' || PG_ATTRIBUTE.attname || '_seq'
WHERE sequence_schema='public';
重新检查公共模式函数中的所有序列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24CREATE OR REPLACE FUNCTION public.recheck_sequence (
)
RETURNS void AS
$body$
DECLARE
_table_name VARCHAR;
_column_name VARCHAR;
_sequence_name VARCHAR;
BEGIN
FOR _table_name IN SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = 'public' LOOP
FOR _column_name IN SELECT column_name FROM information_schema.columns WHERE TABLE_NAME = _table_name LOOP
SELECT pg_get_serial_sequence(_table_name, _column_name) INTO _sequence_name;
IF _sequence_name IS NOT NULL THEN
EXECUTE 'SELECT setval('''||_sequence_name||''', COALESCE((SELECT MAX('||quote_ident(_column_name)||')+1 FROM '||quote_ident(_table_name)||'), 1), FALSE);';
END IF;
END LOOP;
END LOOP;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;
要重新启动所有序列为1,请使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22-- Create Function
CREATE OR REPLACE FUNCTION"sy_restart_seq_to_1" (
relname TEXT
)
RETURNS"pg_catalog"."void" AS
$BODY$
DECLARE
BEGIN
EXECUTE 'ALTER SEQUENCE '||relname||' RESTART WITH 1;';
END;
$BODY$
LANGUAGE 'plpgsql';
-- Use Function
SELECT
relname
,sy_restart_seq_to_1(relname)
FROM pg_class
WHERE relkind = 'S';
如果在为初始化加载自定义SQL数据时看到此错误,则另一种避免这种情况的方法是:
而不是写:
1INSERT INTO book (id, name, price) VALUES (1 , 'Alchemist' , 10),从初始数据中删除
id (主键)1INSERT INTO book (name, price) VALUES ('Alchemist' , 10),这使Postgres序列保持同步!
这个答案是毛罗的副本。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26DROP FUNCTION IF EXISTS rebuilt_sequences() RESTRICT;
CREATE OR REPLACE FUNCTION rebuilt_sequences() RETURNS INTEGER AS
$body$
DECLARE sequencedefs RECORD; c INTEGER ;
BEGIN
FOR sequencedefs IN SELECT
DISTINCT(constraint_column_usage.table_name) AS tablename,
constraint_column_usage.column_name AS columnname,
REPLACE(REPLACE(COLUMNS.column_default,'''::regclass)',''),'nextval(''','') AS sequencename
FROM information_schema.constraint_column_usage, information_schema.columns
WHERE constraint_column_usage.table_schema ='public' AND
COLUMNS.table_schema = 'public' AND COLUMNS.table_name=constraint_column_usage.table_name
AND constraint_column_usage.column_name = COLUMNS.column_name
AND COLUMNS.column_default IS NOT NULL
ORDER BY sequencename
LOOP
EXECUTE 'select max('||sequencedefs.columnname||') from ' || sequencedefs.tablename INTO c;
IF c IS NULL THEN c = 0; END IF;
IF c IS NOT NULL THEN c = c+ 1; END IF;
EXECUTE 'alter sequence ' || sequencedefs.sequencename ||' minvalue '||c ||' start ' || c ||' restart with ' || c;
END LOOP;
RETURN 1; END;
$body$ LANGUAGE plpgsql;
SELECT rebuilt_sequences();
我花了一个小时试图让djsnowsill的答案与使用混合案例表和列的数据库一起工作,然后由于Manuel Darveau的评论最终偶然发现了解决方案,但我想我可以让每个人都清楚一点:
1
2
3
4
5
6
7
8
9
10
11
12CREATE OR REPLACE FUNCTION"reset_sequence" (tablename text, columnname text)
RETURNS"pg_catalog"."void" AS
$body$
DECLARE
BEGIN
EXECUTE format('SELECT setval(pg_get_serial_sequence(''%1$I'', %2$L),
(SELECT COALESCE(MAX(%2$I)+1,1) FROM %1$I), false)',tablename,columnname);
END;
$body$ LANGUAGE 'plpgsql';
SELECT format('%s_%s_seq',TABLE_NAME,column_name), reset_sequence(TABLE_NAME,column_name)
FROM information_schema.columns WHERE column_default LIKE 'nextval%';这有以下好处:
- 不假设ID列以特定方式拼写。
- 不假设所有表都有序列。
- 适用于混合案例表/列名称。
- 使用格式更简洁。
要解释一下,问题是
pg_get_serial_sequence 需要字符串来计算你所指的内容,所以如果你这样做:1
2
3"TableName" --it thinks it's a table or column
'TableName' --it thinks it's a string, but makes it lower case
'"TableName"' --it works!这是使用格式字符串中的
''%1$I'' 实现的,'' 使得撇号1$ 表示第一个arg,而I 表示引号
克劳斯的答案是最有用的,有点想念的人:你
必须在select语句中添加DISTINCT。但是,如果您确定没有表+列名称可以等效
对于两个不同的表,您还可以使用:1
2
3
4
5
6
7SELECT sequence_name, --PG_CLASS.relname, PG_ATTRIBUTE.attname
reset_sequence(split_part(sequence_name, '_id_seq',1))
FROM PG_CLASS
JOIN PG_ATTRIBUTE ON PG_ATTRIBUTE.attrelid = PG_CLASS.oid
JOIN information_schema.sequences
ON information_schema.sequences.sequence_name = PG_CLASS.relname || '_' || PG_ATTRIBUTE.attname
WHERE sequence_schema='public';这是user457226解决方案的扩展,适用于何时
一些感兴趣的列名不是'ID'。- ...当然,还需要更改"reset_sequence",即添加"columnname"参数,而不是使用"id"。
1
2
3
4
5
6SELECT '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- 虽然此代码可能会回答这个问题,但提供有关此代码为何和/或如何回答问题的其他背景可提高其长期价值。
丑陋的黑客使用一些shell魔法修复它,不是一个很好的解决方案,但可能激发其他类似的问题:)
1pg_dump -s <DATABASE> | grep 'CREATE TABLE' | awk '{print"SELECT setval(#" $3"_id_seq#, (SELECT MAX(id) FROM" $3"));"}' | sed"s/#/'/g" | psql <DATABASE> -f -
尝试reindex。
更新:正如评论中所指出的,这是对原始问题的回答。
- reindex不起作用,它似乎只是将索引增加1
- reindex没有用,因为它回答了你的原始问题,关于数据库索引,而不是序列
一种更新架构中用作ID的所有序列的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16DO $$ DECLARE
r RECORD;
BEGIN
FOR r IN (SELECT tablename, pg_get_serial_sequence(tablename, 'id') AS sequencename
FROM pg_catalog.pg_tables
WHERE schemaname='YOUR_SCHEMA'
AND tablename IN (SELECT TABLE_NAME
FROM information_schema.columns
WHERE TABLE_NAME=tablename AND column_name='id')
ORDER BY tablename)
LOOP
EXECUTE
'SELECT setval(''' || r.sequencename || ''', COALESCE(MAX(id), 1), MAX(id) IS NOT null)
FROM ' || r.tablename || ';';
END LOOP;
END $$;
SELECT setval... 使得JDBC变成了bork,所以这里有一种与Java兼容的方法:1
2
3-- work around JDBC 'A result was returned when none was expected.'
-- fix broken nextval due to poorly written 20140320100000_CreateAdminUserRoleTables.sql
DO 'BEGIN PERFORM setval(pg_get_serial_sequence(''admin_user_role_groups'', ''id''), 1 + COALESCE(MAX(id), 0), FALSE) FROM admin_user_role_groups; END;';
Copyright © 码农家园 联系:[email protected]