我遇到了我的主键序列与我的表行不同步的问题。
也就是说,当我插入一个新行时,我得到一个重复键错误,因为串行数据类型中隐含的序列返回一个已经存在的数字。
这似乎是由于导入/恢复没有正确维护顺序引起的。
我遇到了我的主键序列与我的表行不同步的问题。
也就是说,当我插入一个新行时,我得到一个重复键错误,因为串行数据类型中隐含的序列返回一个已经存在的数字。
这似乎是由于导入/恢复没有正确维护顺序引起的。
-- 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;
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) 为空,你也不能设置 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
仅当序列由列拥有时才有效。如果递增列被定义为一种serial
类型,则会出现这种情况,但是如果手动添加序列,则必须确保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 setval('tbl_tbl_id_seq', max(tbl_id)) FROM tbl;
tbl_id
作为serial
table 的列tbl
,从序列中提取tbl_tbl_id_seq
(这是默认的自动名称)。
如果您不知道附加序列的名称(不必采用默认形式),请使用pg_get_serial_sequence()
:
SELECT setval(pg_get_serial_sequence('tbl', 'tbl_id'), max(tbl_id)) FROM tbl;
这里没有一个错误。手册:
双参数形式将序列的
last_value
字段设置为指定值并将其is_called
字段设置为true,这意味着 下一个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
在目标表上获取此锁定模式
这将从公共重置所有序列,而不会对表或列名做出任何假设。在 8.4 版本上测试
CREATE 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%';
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;
此命令仅用于更改 postgresql 中自动生成的键序列值
ALTER SEQUENCE "your_sequence_name" RESTART WITH 0;
代替零,您可以输入要重新启动序列的任何数字。
默认序列名称将"TableName_FieldName_seq"
. 例如,如果您的表名称是"MyTable"
并且您的字段名称是"MyID"
,那么您的序列名称将是"MyTable_MyID_seq"
。
这是答案与@murugesanponappan 的答案相同,但他的解决方案中有语法错误。您不能(select max()...)
在alter
命令中使用子查询。因此,要么您必须使用固定数值,要么您需要使用变量来代替子查询。
在下面的示例中,表名是users
,架构名是public
(默认架构),根据您的需要替换它。
SELECT MAX(id) FROM public.users;
SELECT nextval('public."users_id_seq"');
next value
低于max id
,重置它:SELECT setval('public."users_id_seq"',
(SELECT MAX(id) FROM public.users)
);
nextval()
将在返回当前值之前增加序列,而currval()
只返回当前值,如此处所述。
重置所有序列,除了每个表的主键是“id”之外,没有关于名称的假设:
CREATE 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%';
当序列名、列名、表名或模式名包含有趣的字符(如空格、标点符号等)时,这些函数充满了危险。我写了这个:
CREATE 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 传递给单个序列来调用它,它将返回具有默认序列的任何表使用的最大数字;或者您可以使用这样的查询运行它,以重置数据库中的所有序列:
select relname, setval(oid, sequence_max_value(oid))
from pg_class
where relkind = 'S';
使用不同的 qual 您只能重置某个模式中的序列,依此类推。例如,如果您想调整“公共”模式中的序列:
select 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 中看到这样的内容时,就会发生这种情况:
alvherre=# \d baz
Tabla «public.baz»
Columna | Tipo | Modificadores
---------+---------+------------------------------------------------
a | integer | default nextval(('foo_a_seq'::text)::regclass)
请注意,除了 ::regclass 转换之外,该默认子句中的 nextval() 调用还有 ::text 转换。我认为这是由于数据库是从旧 PostgreSQL 版本进行 pg_dump 的。将会发生的是上面的函数 sequence_max_value() 将忽略这样的表。要解决此问题,您可以重新定义 DEFAULT 子句以直接引用序列而不进行强制转换:
alvherre=# alter table baz alter a set default nextval('foo_a_seq');
ALTER TABLE
然后 psql 正确显示:
alvherre=# \d baz
Tabla «public.baz»
Columna | Tipo | Modificadores
---------+---------+----------------------------------------
a | integer | default nextval('foo_a_seq'::regclass)
一旦你解决了这个问题,这个函数就可以正确地用于这个表以及所有其他可能使用相同序列的表。
我建议在 postgres wiki 上找到这个解决方案。它会更新您的表格的所有序列。
SELECT '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):
例子:
psql -Atq -f reset.sql -o temp
psql -f temp
rm temp
原始文章(也修复了序列所有权)在这里
另一个 plpgsql - 仅在以下情况下重置max(att) > then lastval
do --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
给出列表,而不是实际重置值
从公共重置所有序列
CREATE 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';
这个问题发生在我使用实体框架创建数据库然后用初始数据为数据库播种时,这使得序列不匹配。
我通过在为数据库播种后创建要运行的脚本来解决它:
DO
$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$
这里有一些非常核心的答案,我假设在被问到这个问题的时候它曾经非常糟糕,因为这里的很多答案不适用于 9.3 版。自 8.0 版以来的文档为这个问题提供了答案:
SELECT setval('serial', max(id)) FROM distributors;
此外,如果您需要注意区分大小写的序列名称,您可以这样做:
SELECT setval('"Serial"', max(id)) FROM distributors;
我的版本使用第一个,带有一些错误检查......
BEGIN;
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;
把它们放在一起
CREATE 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 通常需要)。
重新检查公共模式函数中的所有序列
CREATE 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;
在我还没有尝试过代码之前:在下面我发布了适用于我的电脑 [Postgres 8.3] 的 Klaus 和 user457226 解决方案的 sql 代码版本,只对 Klaus 和我的版本进行了一些小的调整为user457226 之一。
克劳斯解决方案:
drop 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 解决方案:
--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';
这个答案是 mauro 的副本。
drop 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();
如果在加载自定义 SQL 数据以进行初始化时看到此错误,另一种避免这种情况的方法是:
而不是写:
INSERT INTO book (id, name, price) VALUES (1 , 'Alchemist' , 10),
id
从初始数据中删除(主键)
INSERT INTO book (name, price) VALUES ('Alchemist' , 10),
这使 Postgres 序列保持同步!
要将所有序列重新启动为 1,请使用:
-- 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';
Klaus 的答案是最有用的,只是有点想念:你必须在 select 语句中添加 DISTINCT。
但是,如果您确定对于两个不同的表,没有表+列名可以等效,您也可以使用:
select 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”的情况。
我花了一个小时试图让 djsnowsill 使用混合大小写表和列来处理数据库,然后由于 Manuel Darveau 的评论终于偶然发现了解决方案,但我想我可以让每个人都更清楚一点:
CREATE 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%';
这有以下好处:
解释一下,问题是pg_get_serial_sequence
需要字符串来计算出你所指的内容,所以如果你这样做:
"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 '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
只需运行以下命令:
SELECT setval('my_table_seq', (SELECT max(id) FROM my_table));
尝试重新索引。
更新:正如评论中所指出的,这是对原始问题的答复。
丑陋的黑客使用一些 shell 魔法来修复它,这不是一个很好的解决方案,但可能会激发其他有类似问题的人:)
pg_dump -s <DATABASE> | grep 'CREATE TABLE' | awk '{print "SELECT setval(#" $3 "_id_seq#, (SELECT MAX(id) FROM " $3 "));"}' | sed "s/#/'/g" | psql <DATABASE> -f -
一种更新架构中用作 ID 的所有序列的方法:
DO $$ 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 $$;
所以我可以看出这个线程中没有足够的意见或重新发明的轮子,所以我决定增加一些趣味性。
下面是一个过程:
CREATE OR REPLACE PROCEDURE pg_reset_all_table_sequences(
IN commit_mode BOOLEAN DEFAULT FALSE
, IN mask_in TEXT DEFAULT NULL
) AS
$$
DECLARE
sql_reset TEXT;
each_sec RECORD;
new_val TEXT;
BEGIN
sql_reset :=
$sql$
SELECT setval(pg_get_serial_sequence('%1$s.%2$s', '%3$s'), coalesce(max("%3$s"), %4$s), false) FROM %1$s.%2$s;
$sql$
;
FOR each_sec IN (
SELECT
quote_ident(table_schema) as table_schema
, quote_ident(table_name) as table_name
, column_name
, coalesce(identity_start::INT, seqstart) as min_val
FROM information_schema.columns
JOIN pg_sequence ON seqrelid = pg_get_serial_sequence(quote_ident(table_schema)||'.'||quote_ident(table_name) , column_name)::regclass
WHERE
(is_identity::boolean OR column_default LIKE 'nextval%') -- catches both SERIAL and IDENTITY sequences
-- mask on column address (schema.table.column) if supplied
AND coalesce( table_schema||'.'||table_name||'.'||column_name = mask_in, TRUE )
)
LOOP
IF commit_mode THEN
EXECUTE format(sql_reset, each_sec.table_schema, each_sec.table_name, each_sec.column_name, each_sec.min_val) INTO new_val;
RAISE INFO 'Resetting sequence for: %.% (%) to %'
, each_sec.table_schema
, each_sec.table_name
, each_sec.column_name
, new_val
;
ELSE
RAISE INFO 'Sequence found for resetting: %.% (%)'
, each_sec.table_schema
, each_sec.table_name
, each_sec.column_name
;
END IF
;
END LOOP;
END
$$
LANGUAGE plpgsql
;
预览:
call pg_reset_all_table_sequences();
承诺:
call pg_reset_all_table_sequences(true);
仅指定您的目标表:
call pg_reset_all_table_sequences('schema.table.column');
这里有很多很好的答案。重新加载我的 Django 数据库后,我也有同样的需求。
但我需要:
这似乎与最初的要求非常相似。
感谢 Baldiry 和 Mauro 让我走上了正轨。
drop function IF EXISTS reset_sequences(text[], text) RESTRICT;
CREATE OR REPLACE FUNCTION reset_sequences(
in_schema_name_list text[] = '{"django", "dbaas", "metrics", "monitor", "runner", "db_counts"}',
in_table_name text = '%') RETURNS text[] as
$body$
DECLARE changed_seqs text[];
DECLARE sequence_defs RECORD; c integer ;
BEGIN
FOR sequence_defs IN
select
DISTINCT(ccu.table_name) as table_name,
ccu.column_name as column_name,
replace(replace(c.column_default,'''::regclass)',''),'nextval(''','') as sequence_name
from information_schema.constraint_column_usage ccu,
information_schema.columns c
where ccu.table_schema = ANY(in_schema_name_list)
and ccu.table_schema = c.table_schema
AND c.table_name = ccu.table_name
and c.table_name like in_table_name
AND ccu.column_name = c.column_name
AND c.column_default is not null
ORDER BY sequence_name
LOOP
EXECUTE 'select max(' || sequence_defs.column_name || ') from ' || sequence_defs.table_name INTO c;
IF c is null THEN c = 1; else c = c + 1; END IF;
EXECUTE 'alter sequence ' || sequence_defs.sequence_name || ' restart with ' || c;
changed_seqs = array_append(changed_seqs, 'alter sequence ' || sequence_defs.sequence_name || ' restart with ' || c);
END LOOP;
changed_seqs = array_append(changed_seqs, 'Done');
RETURN changed_seqs;
END
$body$ LANGUAGE plpgsql;
然后执行并查看更改运行:
select *
from unnest(reset_sequences('{"django", "dbaas", "metrics", "monitor", "runner", "db_counts"}'));
退货
activity_id_seq restart at 22
api_connection_info_id_seq restart at 4
api_user_id_seq restart at 1
application_contact_id_seq restart at 20
我找不到 Rails 的明确答案。
来自rails console
ActiveRecord::Base.connection.execute("SELECT setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM table_name;")
替换table_name
为users
例如。
SELECT setval...
使 JDBC 很糟糕,所以这是一种与 Java 兼容的方法:
-- 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;';