3

我对在下面显示的普通数据库中遇到的一个奇怪的 Postgres 问题感到困惑:如果我首先插入一个标签并明确指定其 ID,然后尝试在传递 ID 的情况下插入另一个标签,那么第二次插入将失败。如果我第三次尝试(再次没有 ID),插入成功。

DROP DATABASE IF EXISTS mydb;
CREATE DATABASE mydb;

\c mydb

DROP SCHEMA public;
CREATE SCHEMA core;

CREATE TABLE core.tag
(
    id serial PRIMARY KEY,
    title text NOT NULL
);

-- this works: all columns specified explicitly
INSERT INTO core.tag(id, title) VALUES (1, 'known tag');

-- omitting the tag ID fails with
-- ERROR:  duplicate key value violates unique constraint "tag_pkey"
-- DETAIL:  Key (id)=(1) already exists.
INSERT INTO core.tag(title) VALUES ('unknown tag');

-- this works again ?!?
INSERT INTO core.tag(title) VALUES ('unknown tag');

该问题似乎只发生在新创建的数据库上,一旦发生,它似乎不会再次发生。我从来没有遇到过这样的事情——到目前为止,我刚刚插入了带有或不带有显式 ID 和 AFAICS 的数据,从来没有像这样失败过......

有谁知道这里发生了什么?!?

环境:Mac OSX 10.7.5 上的 PostgreSQL 9.1.3

4

1 回答 1

7

当然,这失败了。

怎么了?

创建表时,还会创建一个序列,用于生成 ID 列的值。该序列从 1 开始,但仅在为 ID 列指定值时使用。

现在当你跑

INSERT INTO core.tag(id, title) VALUES (1, 'known tag');

你绕过 Postgres 对 ID 值的自动分配,序列“保持”在一个。

现在当你跑

INSERT INTO core.tag(title) VALUES ('unknown tag');

Postgres 从序列中获取下一个值——即 1。但该值已经存在,因此插入失败。从序列中取值后,下一个值为2,因此后续不指定ID值的插入获取2并成功。

解决方案是永远不要在插入中包含 ID 列。或者 - 如果你这样做 - 从序列中请求 ID:

INSERT INTO core.tag(id, title) VALUES (nextval('tag_id_seq'), 'known tag');

创建序列列时,它会自动与名为 的序列关联<table_name>_<column_name>_seq。这就是我在上述声明中使用的名称。

有关串行“数据类型”如何工作的更多详细信息,请参见手册:http ://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-SERIAL

于 2013-01-11T08:52:56.087 回答