如何重置postgres中的序列并用新数据填充id列?


126

我有一个超过一百万行的表。我需要重置序列并使用新值(1、2、3、4 ...等)重新分配ID列。有什么简单的方法吗?


6
真正的问题:您到底为什么要这样做?大概ID是主键,因此更改主键没有任何好处。主键是无意义的(在您的情况下是人造的)值。“重新编号”在关系数据库中没有任何明智的用途。
a_horse_with_no_name 2011年

2
最初,我的应用程序在本地运行,然后将数据复制到生产环境中。但是ids并不是从1开始的。所以排序结果如下:150、151 ...,300、1、2 ...如果我没有重新编号,最终会导致重复的id错误。 ID。此外,order by id通常比order by好created_at。这就是对我有用的东西
x-yuri 2015年

这样做的目的是,您可以继续对数据库中的主键使用常规int而不是bigint,该数据库会继续增加顺序键,但会不断接收新数据。您将很快遇到带符号的整数限制,如果保留现存的id并不重要,则此过程将使您返回可管理的id数。
本·威尔逊

另一个用途是测试。您想在开始每次测试之前将表重置为已知状态,并且该表需要重置ID。
Safa Alai

Answers:


203

如果您不想保留ID的顺序,则可以

ALTER SEQUENCE seq RESTART WITH 1;
UPDATE t SET idcolumn=nextval('seq');

我怀疑是否有一种简单的方法可以按您选择的顺序执行此操作,而无需重新创建整个表。


4
那不是ALTER SEQUENCE seq RESTART WITH 1;吗?
拉尔斯·豪格斯

5
这可能会导致重复的ID。为防止这种情况,您可以先将所有值都设置为非常高的值:UPDATE t SET idcolumn = 1000000 + nextval('seq'); 然后运行上面的脚本。
tahagh 2013年

5
SELECT setval('seq', 1, FALSE)应该做同样的事情(这里,第三个参数FALSE做魔术,因为它表明nextval必须是1而不是2)
Vasilen Donchev

@VassilenDontchev,绝对。
Michael Krelin-黑客

55

在PostgreSQL 8.4或更高版本中,不再需要指定WITH 1。将使用CREATE SEQUENCE由记录或最后设置的起始值ALTER SEQUENCE START WITH(最有可能是1)。

重置顺序:

ALTER SEQUENCE seq RESTART;

然后更新表的ID列:

UPDATE foo SET id = DEFAULT;

资料来源:PostgreSQL文件


3
这似乎是最好的答案,因为它避免了对序列起始值的假设。
牧羊犬

我的情况的最佳答案。我这个答案结合了这一个,这也解释了ALTER SEQUENCE命令,所以我改变“序列”由 mytable_id_seq其中“MYTABLE”是我的表名和“身份证”是我的连载专栏的名称
哈维-

42

重置顺序:

SELECT setval('sequence_name', 0);

更新当前记录:

UPDATE foo SET id = DEFAULT;

3
序列可以有一个最低值大于0(和默认最小值按类型使用serialCREATE SEQUENCE为1!)
BRK

18

两者都提供的解决方案对我不起作用;

> SELECT setval('seq', 0);
ERROR:  setval: value 0 is out of bounds for sequence "seq" (1..9223372036854775807)

setval('seq', 1)从2开始编号,也ALTER SEQUENCE seq START 1从2 开始编号,因为seq.is_call为true(Postgres版本9.0.4)

对我有用的解决方案是:

> ALTER SEQUENCE seq RESTART WITH 1;
> UPDATE foo SET id = DEFAULT;

1
这里同样的问题。您的解决方案也适用于PostgreSQL 8.3.10。
PeqNP

17

只是为了简化和阐明用于重置序列的ALTER SEQUENCESELECT setval的正确用法:

ALTER SEQUENCE sequence_name RESTART WITH 1;

相当于

SELECT setval('sequence_name', 1, FALSE);

无论是陈述的,可以使用重置序列,你可以得到由NEXTVAL(“SEQUENCE_NAME”)的下一个值规定在这里也:

nextval('sequence_name')

谢谢阿里。我刚刚注意到,使用setval函数将第3个参数设置为false非常重要
DavidHyogo,

13

重设从1开始的序列的最佳方法是执行以下操作:

ALTER SEQUENCE <tablename>_<id>_seq RESTART WITH 1

因此,例如对于用户表,它将是:

ALTER SEQUENCE users_id_seq RESTART WITH 1

6

要保留行的顺序:

UPDATE thetable SET rowid=col_serial FROM 
(SELECT rowid, row_number() OVER ( ORDER BY lngid) AS col_serial FROM thetable ORDER BY lngid) AS t1 
WHERE thetable.rowid=t1.rowid;

4

仅供参考:如果您需要在一系列ID(例如256-10000000)之间指定新的起始值:

SELECT setval('"Sequence_Name"', 
       (SELECT coalesce(MAX("ID"),255) 
           FROM "Table_Name" 
           WHERE "ID" < 10000000 and "ID" >= 256)+1
       ); 

2

仅重置序列并更新所有行可能会导致重复的id错误。在许多情况下,您必须将所有行更新两次。首先使用较高的ID以避免重复,然后使用您实际想要的ID。

请避免为所有ID添加固定金额(如其他评论所建议)。如果行数超过此固定数量会怎样?假设序列的下一个值高于现有行的所有id(您只想填补空白),我会这样做:

UPDATE table SET id = DEFAULT;
ALTER SEQUENCE seq RESTART;
UPDATE table SET id = DEFAULT;


0

受其他答案的启发,我创建了一个SQL函数来进行序列迁移。该函数将主键序列移动到新的连续序列,该序列以现有序列范围之内或之外的任何值(> = 1)开头。

我将在此处解释如何在将具有相同架构但值不同的两个数据库迁移到一个数据库中时使用此功能。

首先,该函数(将打印生成的SQL命令,以便清楚实际发生了什么):

CREATE OR REPLACE FUNCTION migrate_pkey_sequence
  ( arg_table      text
  , arg_column     text
  , arg_sequence   text
  , arg_next_value bigint  -- Must be >= 1
  )
RETURNS int AS $$
DECLARE
  result int;
  curr_value bigint = arg_next_value - 1;
  update_column1 text := format
    ( 'UPDATE %I SET %I = nextval(%L) + %s'
    , arg_table
    , arg_column
    , arg_sequence
    , curr_value
    );
  alter_sequence text := format
    ( 'ALTER SEQUENCE %I RESTART WITH %s'
    , arg_sequence
    , arg_next_value
    );
  update_column2 text := format
    ( 'UPDATE %I SET %I = DEFAULT'
    , arg_table
    , arg_column
    );
  select_max_column text := format
    ( 'SELECT coalesce(max(%I), %s) + 1 AS nextval FROM %I'
    , arg_column
    , curr_value
    , arg_table
    );
BEGIN
  -- Print the SQL command before executing it.
  RAISE INFO '%', update_column1;
  EXECUTE update_column1;
  RAISE INFO '%', alter_sequence;
  EXECUTE alter_sequence;
  RAISE INFO '%', update_column2;
  EXECUTE update_column2;
  EXECUTE select_max_column INTO result;
  RETURN result;
END $$ LANGUAGE plpgsql;

该函数migrate_pkey_sequence采用以下参数:

  1. arg_table:表格名称(例如'example'
  2. arg_column:主键列名称(例如'id'
  3. arg_sequence:序列名称(例如'example_id_seq'
  4. arg_next_value:迁移后列的下一个值

它执行以下操作:

  1. 将主键值移到自由范围。我假设 nextval('example_id_seq')遵循max(id),并且序列从1开始。这也处理的情况arg_next_value > max(id)
  2. 将主键值移动到以开头的连续范围 arg_next_value。键值的顺序被保留,但范围中的孔未被保留。
  3. 打印序列中的下一个值。如果要迁移另一个表的列并与该表合并,这将很有用。

为了演示,我们使用定义如下的序列和表(例如使用psql):

# CREATE SEQUENCE example_id_seq
  START WITH 1
  INCREMENT BY 1
  NO MINVALUE
  NO MAXVALUE
  CACHE 1;
# CREATE TABLE example
  ( id bigint NOT NULL DEFAULT nextval('example_id_seq'::regclass)
  );

然后,我们插入一些值(例如从3开始):

# ALTER SEQUENCE example_id_seq RESTART WITH 3;
# INSERT INTO example VALUES (DEFAULT), (DEFAULT), (DEFAULT);
-- id: 3, 4, 5

最后,我们迁移这些example.id值以1开始。

# SELECT migrate_pkey_sequence('example', 'id', 'example_id_seq', 1);
INFO:  00000: UPDATE example SET id = nextval('example_id_seq') + 0
INFO:  00000: ALTER SEQUENCE example_id_seq RESTART WITH 1
INFO:  00000: UPDATE example SET id = DEFAULT
 migrate_pkey_sequence
-----------------------
                     4
(1 row)

结果:

# SELECT * FROM example;
 id
----
  1
  2
  3
(3 rows)

0

即使auto-increment列也不是PK(在此示例中为seq-aka sequence),您也可以通过触发器来实现:

如果存在则删除表devops_guide CASCADE;

SELECT 'create the "devops_guide" table'
;
   CREATE TABLE devops_guide (
      guid           UUID NOT NULL DEFAULT gen_random_uuid()
    , level          integer NULL
    , seq            integer NOT NULL DEFAULT 1
    , name           varchar (200) NOT NULL DEFAULT 'name ...'
    , description    text NULL
    , CONSTRAINT pk_devops_guide_guid PRIMARY KEY (guid)
    ) WITH (
      OIDS=FALSE
    );

-- START trg_devops_guide_set_all_seq
CREATE OR REPLACE FUNCTION fnc_devops_guide_set_all_seq()
    RETURNS TRIGGER
    AS $$
       BEGIN
         UPDATE devops_guide SET seq=col_serial FROM
         (SELECT guid, row_number() OVER ( ORDER BY seq) AS col_serial FROM devops_guide ORDER BY seq) AS tmp_devops_guide
         WHERE devops_guide.guid=tmp_devops_guide.guid;

         RETURN NEW;
       END;
    $$ LANGUAGE plpgsql;

 CREATE TRIGGER trg_devops_guide_set_all_seq
  AFTER UPDATE OR DELETE ON devops_guide
  FOR EACH ROW
  WHEN (pg_trigger_depth() < 1)
  EXECUTE PROCEDURE fnc_devops_guide_set_all_seq();

-1

如果您使用的是pgAdmin3,请展开“序列”,右键单击序列,转到“属性”,然后在“定义”选项卡中将“当前值”更改为所需的任何值。无需查询。


3
如果您至少不告诉我们您使用的是什么工具,那么您的答案毫无价值。
11101101b 2014年

3
这是最简单的方法,显然我认为他是在说pg admin 3
MvcCmsJon 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.