关于自动增量:PostgreSQL Autoincrement

PostgreSQL Autoincrement

我正在从mysql切换到postgresql,我想知道如何实现自动增量值。我在PostgreSQL文档中看到了一个数据类型"serial",但在使用它时(在V8.0中)会出现语法错误。


是的,串行是等效功能。

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE foo (
id SERIAL,
bar VARCHAR);

INSERT INTO foo (bar) VALUES ('blah');
INSERT INTO foo (bar) VALUES ('blah');

SELECT * FROM foo;

1,blah
2,blah

序列只是围绕序列创建表时间宏。不能将序列号更改为现有列。


您可以使用任何其他整数数据类型,如smallint

例子:

1
2
3
4
5
CREATE SEQUENCE user_id_seq;
CREATE TABLE USER (
    user_id SMALLINT NOT NULL DEFAULT NEXTVAL('user_id_seq')
);
ALTER SEQUENCE user_id_seq OWNED BY USER.user_id;

最好使用自己的数据类型,而不是用户串行数据类型。


如果要将序列添加到表中已存在的ID中,可以使用:

1
2
CREATE SEQUENCE user_id_seq;
ALTER TABLE USER ALTER user_id SET DEFAULT NEXTVAL('user_id_seq');


虽然看起来序列相当于mysql auto_increment,但有一些细微但重要的区别:

1。失败的查询增加序列/序列

对于失败的查询,序列列将递增。这会导致失败查询的碎片化,而不仅仅是行删除。例如,在PostgreSQL数据库上运行以下查询:

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE table1 (
  uid serial NOT NULL PRIMARY KEY,
  col_b INTEGER NOT NULL,
  CHECK (col_b>=0)
);

INSERT INTO table1 (col_b) VALUES(1);
INSERT INTO table1 (col_b) VALUES(-1);
INSERT INTO table1 (col_b) VALUES(2);

SELECT * FROM table1;

您应该得到以下输出:

1
2
3
4
5
 uid | col_b
-----+-------
   1 |     1
   3 |     2
(2 ROWS)

注意uid是如何从1变为3而不是从1变为2的。

如果要手动创建自己的序列,并使用以下项,则仍然会发生这种情况:

1
2
3
4
5
6
7
CREATE SEQUENCE table1_seq;
CREATE TABLE table1 (
    col_a SMALLINT NOT NULL DEFAULT NEXTVAL('table1_seq'),
    col_b INTEGER NOT NULL,
    CHECK (col_b>=0)
);
ALTER SEQUENCE table1_seq OWNED BY table1.col_a;

如果要测试MySQL的不同之处,请在MySQL数据库上运行以下内容:

1
2
3
4
5
6
7
8
CREATE TABLE table1 (
  uid INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  col_b INT UNSIGNED NOT NULL
);

INSERT INTO table1 (col_b) VALUES(1);
INSERT INTO table1 (col_b) VALUES(-1);
INSERT INTO table1 (col_b) VALUES(2);

您应该在不使用碎片的情况下获得以下信息:

1
2
3
4
5
6
7
+-----+-------+
| uid | col_b |
+-----+-------+
|   1 |     1 |
|   2 |     2 |
+-----+-------+
2 ROWS IN SET (0.00 sec)

2。手动设置串行列值可能会导致以后的查询失败。

@trev在之前的回答中指出了这一点。

要模拟此操作,请手动将uid设置为4,稍后将"冲突"。

1
INSERT INTO table1 (uid, col_b) VALUES(5, 5);

表数据:

1
2
3
4
5
6
 uid | col_b
-----+-------
   1 |     1
   3 |     2
   5 |     5
(3 ROWS)

运行另一个插入:

1
INSERT INTO table1 (col_b) VALUES(6);

表数据:

1
2
3
4
5
6
 uid | col_b
-----+-------
   1 |     1
   3 |     2
   5 |     5
   4 |     6

现在,如果运行另一个插入:

1
INSERT INTO table1 (col_b) VALUES(7);

它将失败,并显示以下错误消息:

ERROR: duplicate key value violates unique constraint"table1_pkey"
DETAIL: Key (uid)=(5) already exists.

相比之下,MySQL会处理得很好,如下所示:

1
INSERT INTO table1 (uid, col_b) VALUES(4, 4);

现在插入另一行而不设置uid

1
INSERT INTO table1 (col_b) VALUES(3);

查询没有失败,uid跳转到5:

1
2
3
4
5
6
7
8
+-----+-------+
| uid | col_b |
+-----+-------+
|   1 |     1 |
|   2 |     2 |
|   4 |     4 |
|   5 |     3 |
+-----+-------+

对MySQL5.6.33、Linux(x86 U64)和PostgreSQL 9.4.9进行了测试。


从Postgres 10开始,还支持SQL标准定义的标识列:

1
2
3
4
CREATE TABLE foo
(
  id INTEGER generated always AS IDENTITY
);

创建一个标识列,除非明确要求,否则不能重写该列。以下插入将失败,列定义为generated always

1
2
INSERT INTO foo (id)
VALUES (1);

但这可以被否决:

1
2
INSERT INTO foo (id) overriding system VALUE
VALUES (1);

当使用选项generated by default时,这基本上是与现有SERIAL实施相同的行为:

1
2
3
4
CREATE TABLE foo
(
  id INTEGER generated BY DEFAULT AS IDENTITY
);

当手动提供一个值时,底层序列也需要手动调整——与SERIAL列相同。

默认情况下,标识列不是主键(就像SERIAL列)。如果它应该是一个,则需要手动定义主键约束。


抱歉,要重新处理一个旧问题,但这是Google上出现的第一个堆栈溢出问题/答案。

这篇文章(首先出现在谷歌上)讨论了如何使用PostgreSQL 10的更新语法:https://blog.2ndquadrant.com/postgresql-10-identity-columns/

恰好是:

1
2
3
CREATE TABLE test_new (
    id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
);

希望有帮助:)


您必须小心不要直接插入序列或序列字段,否则当序列达到插入值时,您的写入将失败:

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
27
28
29
-- Table:"test"

-- DROP TABLE test;

CREATE TABLE test
(
 "ID" SERIAL,
 "Rank" INTEGER NOT NULL,
 "GermanHeadword""text" [] NOT NULL,
 "PartOfSpeech""text" NOT NULL,
 "ExampleSentence""text" NOT NULL,
 "EnglishGloss""text"[] NOT NULL,
  CONSTRAINT"PKey" PRIMARY KEY ("ID","Rank")
)
WITH (
  OIDS=FALSE
);
-- ALTER TABLE test OWNER TO postgres;
 INSERT INTO test("Rank","GermanHeadword","PartOfSpeech","ExampleSentence","EnglishGloss")
           VALUES (1, '{"der","die","das","den","dem","des"}', 'art', 'Der Mann küsst die Frau und das Kind schaut zu', '{"the","of the" }');


 INSERT INTO test("ID","Rank","GermanHeadword","PartOfSpeech","ExampleSentence","EnglishGloss")
           VALUES (2, 1, '{"der","die","das"}', 'pron', 'Das ist mein Fahrrad', '{"that","those"}');

 INSERT INTO test("Rank","GermanHeadword","PartOfSpeech","ExampleSentence","EnglishGloss")
           VALUES (1, '{"der","die","das"}', 'pron', 'Die Frau, die nebenen wohnt, hei?t Renate', '{"that","who"}');

SELECT * FROM test;

在所问问题的上下文中,在@sereja1c对注释的回复中,创建EDOCX1[1]隐式地创建序列,因此对于上述示例-

1
CREATE TABLE foo (id SERIAL,bar VARCHAR);

CREATE TABLE将隐式地为序列列foo.id创建序列foo_id_seq。因此,除非您的ID需要一个特定的数据类型,否则SERIAL4个字节〕有利于它的易用性。


这种方法肯定会奏效,我希望它有助于:

1
2
3
4
5
6
7
8
9
10
CREATE TABLE fruits(
   id SERIAL PRIMARY KEY,
   name VARCHAR NOT NULL
);

INSERT INTO fruits(id,name) VALUES(DEFAULT,'apple');

OR

INSERT INTO fruits VALUES(DEFAULT,'apple');

您可以在下一个链接中查看详细信息:http://www.postgresqltudio.com/postgresql-serial/


自PostgreSQL 10以来

1
2
3
4
CREATE TABLE test_new (
    id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    payload text
);