我遇到了我的主键序列与我的表行不同步的问题。

也就是说,当我插入新行时,我得到重复的键错误,因为串行数据类型中隐含的序列返回了一个已经存在的数字。

由导入/还原导致无法正确维护序列。

评论

我很好奇..您是否在还原之前删除了数据库?我对这种情况有微弱的回忆,但我可能是错的:P

PostgreSQL Wiki上有一个有关修复序列的页面。

只是为了提高可搜索性,这里引发的错误消息是:“重复的键值违反了唯一约束...”

这就是Django中sqlsequencereset的执行方式:SELECT setval(pg_get_serial_sequence(“ ”,'id'),coalesce(max(“ id”),1),max(“ id”)IS null)FROM“ < table_name>“;

的第一个实例需要用单引号引起来,以便pg_get_serioal_sequence函数起作用:SELECT setval(pg_get_serial_sequence('','id'),coalesce(max(“ id”),1) ,max(“ id”)不为null)FROM“ <表名>”

#1 楼

-- 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论坛

评论


无论如何,将1加到MAX(id)将在ID中留下单个数字间隔,因为setval设置的是序列的最后一个值,而不是下一个。

– mikl
09年12月31日上午10:18

如果表中没有行,则您的示例将不起作用。因此,下面给出的SQL更安全:SELECT setval('your_table_id_seq',coalesce((从your_table中选择max(id)+1),1),true);

– Valery Viktorovsky
2012年2月8日在17:52



@Valery:但是为了避免@mikl上面提到的两个注释所造成的差距,您需要SELECT setval('your_table_id_seq',coalesce((从your_table中选择max(id)+1),1),false);

– Antony Hatchkins
2012年11月9日12:19

解决了所有问题并合并为一个查询:SELECT setval('your_seq',(SELECT GREATEST(MAX(your_id)+ 1,nextval('your_seq'))-1 FROM your_table))

–芬西
13-10-27在17:34

如果您的应用程序关心序列中的间隔,则您的应用程序已损坏。序列中的间隙是正常的,并且可能由于计划外的数据库关闭,错误后的事务回滚等而发生。

–克雷格·林格(Craig Ringer)
17年9月20日在2:04

#2 楼

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)为null,所以不能正确处理空表,也不能将setval设置为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的使用受到限制,因为序列名称和重新启动值不能为表达式。

似乎最好的通用解决方案是使用第三个参数false调用setval,从而允许我们指定“要使用的下一个值”:

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类型用于表创建,则所有操作都应:

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;


评论


查询中无需在“ +1”中进行设置,setval()会设置当前值,nextval()已返回当前值+1。

– Antony Hatchkins
2012年11月9日在11:43



包装此方法的函数需要一个参数-table_name-在下面的答案中:stackoverflow.com/a/13308052/237105

– Antony Hatchkins
2012年11月9日15:17

@AntonyHatchkins欢呼。刚刚看到了+1错误的另一个重复,所以终于扫平了,我希望

– tardate
2014年12月14日下午0:26

您还可以在需要时指定架构名称:pg_get_serial_sequence('schema_name.table_name','id')

–马吉德
6月21日7:46



最后一行有错字,最后应该是t2

–AConsumer
7月2日7:01

#3 楼

最短和最快的方法:
SELECT setval('tbl_tbl_id_seq', max(tbl_id)) FROM tbl;

tbl_id是表serialtbl列,是从序列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(除非自定义)。
并发性
无法防御并发序列活动或写入表中的表以上查询呢。如果相关,您可以将表锁定为独占模式。当您尝试同步时,它可以防止并发事务写入更多的数字。 (它还临时阻止了无损写操作,不会破坏最大数量。)
但是,它没有考虑到客户端可能已经预先获取了序列号而没有在主表上进行任何锁定的情况(这可能会发生)。为此,也只增加序列的当前值,而不要减少它。可能看起来有些偏执,但这符合序列的性质并能防止并发问题。
BEGIN;

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。's)是基本功能!如何解决PostgreSQL中缺少标准库的问题????

– Peter Krauss
17-3-21在16:26



没关系,没关系。顺序间隙正常。如果您的应用程序无法应对,则说明您的应用程序已损坏,因为由于事务回滚,计划外的服务器关闭等原因也会引起差距。

–克雷格·林格(Craig Ringer)
17-09-20在2:06

@Craig:我要解决的错位错误(并且不存在)很重要,因为否则我们将冒重复键错误的风险。您考虑的方向相反;似乎是一种误会。

–欧文·布兰德斯特(Erwin Brandstetter)
17 Sep 20 '23:18



嗯,很有道理。

–克雷格·林格(Craig Ringer)
17年9月21日在0:50

好答案!需要注意的是,这些示例令人困惑,因为表名和列名是如此相似……这是表“ roles”和顺序列“ id”的更新示例:SELECT setval('roles_id_seq',max(id))FROM角色;

–坂野真舞
10月17日12:00

#4 楼

这将使公开的所有序列复位,而无需假设表或列的名称。已在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%';


评论


+1非常有用的功能!我们的序列名与表名不完全匹配,因此我使用substring(column_default,'''(。*)''')代替table_name || '_'|| column_name || '_seq'。完美运作。

–克里斯·勒彻(Chris Lercher)
2012年3月28日上午10:05

请注意,这将失败,因为序列名称包含单引号,或者表名称中包含大写,空格等。 quote_literal和quote_ident函数,或者最好是format函数,应该在这里真正使用。

–克雷格·林格(Craig Ringer)
13年7月18日在10:57

希望我能多投一票...先生,很好。至少对我来说,在Postgres 9.1上也能很好地工作。

– Peelman
2014年5月8日在12:23

这很棒。我使用substring(来自'nextval \(''(。+)'':: regclass \)'的column_default)显式获取序列名称。像魅力一样工作。

–马修·麦克唐纳(Matthew MacDonald)
17年2月27日在18:03

我搜索该解决方案已经超过一天了,非常感谢,即使我使用@ChrisLercher建议的方法来替换文本substring(column_default,'''(。*)''')而不是table_name | | '_'|| column_name || '_seq'

– Sushin Pv
4月30日18:00

#5 楼

ALTER SEQUENCE sequence_name重新启动(从table_name中选择SELECT max(id));
不起作用。

从@tardate复制答案:

SELECT 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
2010-09-13 8:12



Muruges的解决方案在9.4中也不起作用。不明白为什么在这个答案上这么多赞。 ALTER SEQUENCE不允许子查询。 @tardate的解决方案效果很好。编辑答案以删除不正确的数据。

–弗拉迪斯拉夫·拉斯特鲁斯尼
2014-12-12 12:28



ALTER SEQUENCE对我来说很完美。我曾经使用COPY引入了一些数据,并且主键中存在空白,而INSERT则抛出了重复的键异常。设置顺序就可以了。 9.4

–user542319
2015年11月10日,0:13

您还可以在需要时指定架构名称:pg_get_serial_sequence('schema_name.table_name','id')

–马吉德
6月21日7:47

#6 楼

此命令仅更改postgresql中自动生成的键序列值

ALTER SEQUENCE "your_sequence_name" RESTART WITH 0;


代替零,您可以输入要从其重新启动序列的任何数字。

默认序列名称为"TableName_FieldName_seq"。例如,如果您的表名是"MyTable",而字段名是"MyID",那么您的序列名将是"MyTable_MyID_seq"

此答案与@murugesanponappan的答案相同,但是他的语法存在错误解。您不能在(select max()...)命令中使用子查询alter。因此,要么必须使用固定的数值,要么需要使用变量来代替子查询。

评论


这是一个完美的解决方案,非常感谢先生。但就我而言,我遇到了一个错误,因此我不得不将其更改为ALTER SEQUENCE“ your_sequence_name” RESTART WITH 1;

– Deunz
16 Dec 20'在15:09

#7 楼

重置所有序列,除了每个表的主键均为“ 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%';


评论


在我的9.1版本上完美工作

–瓦伦丁·瓦西里耶夫(Valentin Vasilyev)
13年4月16日在9:33

如果表包含大写字母,则需要添加引号:pg_get_serial_sequence(“”'||表名||'“''

– Manuel Darveau
2015年5月14日下午3:11

这是最好的功能!您可以使用格式来避免引号问题(并提高优雅度),例如EXECUTE format('SELECT setval(pg_get_serial_sequence(%L,%L),coalesce(max(id),0)+ 1,false)FROM%I;' ,$ 1,$ 2,$ 1);

– Peter Krauss
17年3月21日在16:21



#8 楼

当序列名称,列名称,表名称或模式名称具有有趣的字符(例如空格,标点符号等)时,这些功能将充满风险。我已经写了这个:

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 = 
                                            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';


使用不同的质量,您只能重置特定序列中的序列。架构等。例如,如果要在“公共”模式中调整序列:

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)


请注意,该默认子句中的nextval()调用除了具有:: text强制转换之外:: regclass强制转换。我认为这是由于数据库已从旧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)


修复该问题后,此函数将对该表以及所有其他可能使用相同序列的表正常工作。

评论


这真是太棒了!应该注意的是,我需要在赋值(函数代码中的第21行)处添加一个强制类型转换,例如:newmax:= r.max :: bigint;使它对我来说正常工作。

–汤米·布拉沃(Tommy Bravo)
17年2月8日在12:28

也必须更改此设置:'SELECT max('|| quote_ident(colname)||')FROM'=>'SELECT max('|| quote_ident(colname)||':: bigint)FROM'注意添加的内容: :bigint在动态构建查询中强制转换。

–汤米·布拉沃(Tommy Bravo)
17-2-8在12:44



#9 楼

重置所有来自public

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';

的序列

评论


看来这种方法对列名和表名进行了假设,所以对我不起作用

–djsnowsill
2010年11月4日在21:03

那会不会破坏数据库中的数据?

– Zennin
17年2月10日在13:35

#10 楼

我建议在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):


将其保存到文件,说'reset.sql'
运行文件并以不包含常规标头的方式保存其输出,然后运行该输出。示例:

示例:

psql -Atq -f reset.sql -o temp
psql -f temp
rm temp


原始文章(也已修复序列所有权)在这里

评论


哦,那是一个愚蠢的错误,就我而言,数据已迁移到了postgres DB而不是哨兵中。希望对别人有帮助

– Neo
9月5日7:16



#11 楼

还有另一个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会给出列表,而不是实际上重置值

#12 楼

使用实体框架创建数据库,然后使用初始数据为数据库播种时,会发生此问题,这会导致序列不匹配。

我通过创建在种子数据库后运行的脚本来解决该问题:

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$


评论


为什么MAX(“ Id”)+ 1在序列= =最大值时最适合我。

–lastlink
18年7月24日在11:47

#13 楼

这里有一些非常棘手的答案,我假设它在被问到的时候曾经是很糟糕的,因为这里的很多答案都不适用于9.3版。从8.0版开始的文档就提供了以下问题的答案:

SELECT setval('serial', max(id)) FROM distributors;


此外,如果需要注意区分大小写的序列名,也可以使用此方法:

SELECT setval('"Serial"', max(id)) FROM distributors;


#14 楼

我的版本使用第一个,并进行了一些错误检查...

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;


评论


感谢您的错误检查!非常感谢表/列名过长而被截断,这是RAISE WARNING为我确定的。

–尼古拉斯·莱利(Nicholas Riley)
2012年2月11日,1:16

#15 楼

将所有内容放在一起

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是必需的)。

#16 楼

重新检查公共模式功能中的所有序列

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;


#17 楼

在我尚未尝试代码之前:在下面的文章中,我发布了
适用于我的PC [Postgres 8.3]的Klaus和user457226解决方案的sql代码的版本

适用于Klaus,我的版本适用于user457226。

Klaus解决方案:

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';


#18 楼

此答案是毛罗的副本。

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();


评论


这可以很好地作为单遍脚本来修复数据库中的所有序列

– Freeman Helmuth
6月18日下午16:34

#19 楼

要将所有序列重新启动为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';


#20 楼

克劳斯(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”。

评论


...当然,还需要更改“ reset_sequence”,即添加“ columnname”参数,以代替“ id”使用。

–毛罗
2011年3月9日21:10

#21 楼

如果在加载用于初始化的自定义SQL数据时看到此错误,则另一种避免这种情况的方法是:

而不是编写:

INSERT INTO book (id, name, price) VALUES (1 , 'Alchemist' , 10),


从初始数据中删除id(主键)

INSERT INTO book (name, price) VALUES ('Alchemist' , 10),


这使Postgres序列保持同步!

#22 楼

我花了一个小时试图获得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%';


这样做的好处是:


不假设ID列的拼写是特定的方式。表具有序列。
用于混合大小写表/列名。
使用格式更简洁。

解释一下,问题是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意味着在引号中

#23 楼

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


评论


尽管此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的其他上下文,可以改善其长期价值。

– yeya
19 Mar 10 '19 at 17:05

#24 楼

尝试重新索引。

更新:如评论中所指出,这是对原始问题的答复。

评论


重新索引不起作用,它似乎只能将索引增加1

–麦尔
08-10-28在18:26

重新索引无法正常工作,因为它正在回答您最初的问题,关于数据库索引,而不是序列

– Vinko Vrsalovic
08-10-28在18:28

#25 楼

使用一些外壳魔术来解决问题的丑陋技巧,虽然不是很好的解决方案,但可能会激发其他人遇到类似问题:)

pg_dump -s <DATABASE> | grep 'CREATE TABLE' | awk '{print "SELECT setval(#"  "_id_seq#, (SELECT MAX(id) FROM "  "));"}' | sed "s/#/'/g" | psql <DATABASE> -f -


#26 楼

只需在以下命令下运行:

SELECT setval('my_table_seq', (SELECT max(id) FROM my_table));


#27 楼

一种更新模式中用作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 $$;
 


#28 楼

这里有很多很好的答案。重新加载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


#29 楼

因此,我可以确定该线程中没有足够的意见或重新发明轮子,因此我决定为事情加点趣味。
下面是一个过程:

(仅影响)与表关联的序列
对于SERIAL和GENERATED AS IDENTITY列均有效。
对于good_column_names和“ BAD_column_123”名称均有效。
如果表为空,则自动分配相应序列的已定义起始值< br允许仅影响特定序列(在schema.table.column表示法中)
具有预览模式

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');

#30 楼

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;';


最近发表