在 Postgres 的现有列中添加“ Series”

我在 Postgres 9.0数据库中有一个小表(大约30行) ,其中有一个整数 ID 字段(主键) ,目前包含从1开始的唯一的连续整数,但是没有使用“ Series”关键字创建。

我如何改变这个表,以便从现在开始,对这个表的插入将导致这个字段的行为,好像它已经创建了“串行”作为一种类型?

175833 次浏览

查看以下命令(特别是注释块)。

DROP TABLE foo;
DROP TABLE bar;


CREATE TABLE foo (a int, b text);
CREATE TABLE bar (a serial, b text);


INSERT INTO foo (a, b) SELECT i, 'foo ' || i::text FROM generate_series(1, 5) i;
INSERT INTO bar (b) SELECT 'bar ' || i::text FROM generate_series(1, 5) i;


-- blocks of commands to turn foo into bar
CREATE SEQUENCE foo_a_seq;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');
ALTER TABLE foo ALTER COLUMN a SET NOT NULL;
ALTER SEQUENCE foo_a_seq OWNED BY foo.a;    -- 8.2 or later


SELECT MAX(a) FROM foo;
SELECT setval('foo_a_seq', 5);  -- replace 5 by SELECT MAX result


INSERT INTO foo (b) VALUES('teste');
INSERT INTO bar (b) VALUES('teste');


SELECT * FROM foo;
SELECT * FROM bar;

您也可以使用 START WITH从一个特定的点开始一个序列,尽管 setval 完成同样的事情,如在欧拉的回答中,例如,

SELECT MAX(a) + 1 FROM foo;
CREATE SEQUENCE foo_a_seq START WITH 12345; -- replace 12345 with max above
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');

DR

这里有一个版本,您不需要人工读取一个值并自己键入它。

CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');

另一种选择是在这个答案的末尾使用可重用的 Function


非交互式解决方案

对于那些需要使用 非交互式脚本创建这些 Sequence的人来说,我们只需要添加另外两个答案,例如,修补一个 live-ish DB。

也就是说,当您不想手动输入 SELECT值并自己将其输入到后续的 CREATE语句中时。

简而言之,你可以这样做:

CREATE SEQUENCE foo_a_seq
START WITH ( SELECT max(a) + 1 FROM foo );

... 因为 CREATE SEQUENCE中的 START [WITH]子句需要的是 价值,而不是子查询。

注意: 根据经验,这适用于 PgSQL AFAIK 中所有非 CRUD 语句(也就是说。: INSERTSELECTUPDATEDELETE以外的任何语句)。

但是,setval()可以! 因此,以下内容是绝对可以接受的:

SELECT setval('foo_a_seq', max(a)) FROM foo;

如果没有数据,你不想知道它,使用 coalesce()设置默认值:

SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo;
--                         ^      ^         ^
--                       defaults to:       0

但是,将当前序列值设置为 0即使不是非法的,也是很麻烦的。
使用 setval的三个参数形式会更合适:

--                                             vvv
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
--                                                  ^   ^
--                                                is_called

setval的可选第三个参数设置为 false将防止下一个 nextval在返回值之前推进序列,因此:

下一个 nextval将准确地返回指定的值,并且序列推进将从下面的 nextval开始。

来自 文件中的这个条目

另外,你也可以直接用 CREATE指定拥有 Sequence的列,以后不必修改它:

CREATE SEQUENCE foo_a_seq OWNED BY foo.a;

总之:

CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');

使用 Function

或者,如果计划对多个列执行此操作,则可以选择使用实际的 Function

CREATE OR REPLACE FUNCTION make_into_serial(table_name TEXT, column_name TEXT) RETURNS INTEGER AS $$
DECLARE
start_with INTEGER;
sequence_name TEXT;
BEGIN
sequence_name := table_name || '_' || column_name || '_seq';
EXECUTE 'SELECT coalesce(max(' || column_name || '), 0) + 1 FROM ' || table_name
INTO start_with;
EXECUTE 'CREATE SEQUENCE ' || sequence_name ||
' START WITH ' || start_with ||
' OWNED BY ' || table_name || '.' || column_name;
EXECUTE 'ALTER TABLE ' || table_name || ' ALTER COLUMN ' || column_name ||
' SET DEFAULT nextVal(''' || sequence_name || ''')';
RETURN start_with;
END;
$$ LANGUAGE plpgsql VOLATILE;

像这样使用它:

INSERT INTO foo (data) VALUES ('asdf');
-- ERROR: null value in column "a" violates not-null constraint


SELECT make_into_serial('foo', 'a');
INSERT INTO foo (data) VALUES ('asdf');
-- OK: 1 row(s) affected

PostgreSQL 12为我工作。

它将现有的 int 列转换为串行列。

CREATE SEQUENCE table_name_id_seq;
ALTER TABLE table_name ALTER COLUMN id SET DEFAULT nextval('table_name_id_seq');
ALTER TABLE table_name ALTER COLUMN id SET NOT NULL;
ALTER SEQUENCE table_name_id_seq OWNED BY table_name.id;
SELECT setval('table_name_id_seq', (SELECT max(id) FROM table_name));

如果使用 DBMS,可以将列上的默认值设置为 顺序

default options

可能还有更高级的选择。

advanced default options

PostgreSQL 10及更高版本

不要使用 serial 。感谢 上述评论

ALTER TABLE mysmalltable
ALTER COLUMN id
ADD GENERATED BY DEFAULT AS IDENTITY;