PostgreSQL 中的从属 SERIAL 列

Posted

技术标签:

【中文标题】PostgreSQL 中的从属 SERIAL 列【英文标题】:Dependent SERIAL column in PostgreSQL 【发布时间】:2015-08-10 05:17:13 【问题描述】:

我想在我的表中得到这个结果contacts

 |contact_id |  user_id         |  user_contact_id     |
 +-----------+------------------+----------------------+
 |  1        |  1               |          1           |
 +-----------+------------------+----------------------+
 |  2        |  1               |          2           |
 +-----------+------------------+----------------------+
 |  3        |  1               |          3           |
 +-----------+------------------+----------------------+
 |  4        |  2               |          1           |
 +-----------+------------------+----------------------+
 |  5        |  2               |          2           |
 +-----------+------------------+----------------------+
 |  6        |  2               |          3           |
 +-----------+------------------+----------------------+
 |  7        |  3               |          1           |
 +-----------+------------------+----------------------+

我将只插入user_id

INSERT INTO contacts (user_id) VALUES ($user_id);

contact_id 将自动递增,因为它是 serial。我希望user_contact_id 也由数据库本身自动填充,因此它在并发写入时是 100% 稳定的。

【问题讨论】:

user_contact_id的值是不是一直从1到3算? 跟进上一个相关问题。 ***.com/questions/30469032/… 由于并发问题,这并非微不足道。你需要非常精确。请澄清在您的问题中:您的 Postgres 版本? (user_id, user_contact_id) 必须是唯一的还是偶尔被骗的可以 - 可以通过 contact_id 来区分。 (会使它很多更简单。)如何处理由于DELETE 和/或UPDATE 而导致的user_contact_id 中的空白,或者它是仅INSERT?序列中的间隙可以吗?您知道在并发负载下,serial 列中会出现预期的空白,对吧?这个用例是 few 还是 many 用户? @ErwinBrandstetter : 我使用的是 Postgres 9.4.1,骗子不是借口,我需要一个严格的阵容,因为我要结合 user_id 使用contact_user_id,所以它必须始终是唯一的 我是否正确阅读了您的简短评论:您不会进一步澄清您的问题吗? 【参考方案1】:
INSERT INTO CONTACTS
    (user_id, user_contact_id)
VALUES
    ($user_id, (SELECT COALESCE(MAX(user_contact_id), 0) + 1 FROM CONTACTS WHERE user_id = $user_id))

【讨论】:

Michael - 这很酷,但是并发性如何,我将在有很多客户将记录插入数据库的情况下使用它 如果有新用户,这也不起作用,因为 MAX 将返回 null 和 null+1 == null。 @SamiKuhmonen 这是真的,我同意,你有什么解决方法吗? @CodeRows 为此可以使用COALESCE,使用COALESCE(MAX(user_contact_id), 0)+1 @CodeRows 您的并发问题是正确的。你不应该使用它——它很容易受到种族歧视。【参考方案2】:

正如其他用户建议的那样,只有 sequence-s 或 serial 类型保证是并发安全的。

如果你真的需要让user_contact_id“重启”每个user_id,也许你可以使用以下视图:

create view contacts_v as
select
  contact_id,
  user_id,
  rank() over (partition by user_id order by contact_id) as user_contact_id
from contacts;

【讨论】:

【参考方案3】:

您应该使用序列作为user_contact_id 的默认值。这正是SERIAL 列类型正在做的事情。

http://www.postgresql.org/docs/9.1/static/datatype-numeric.html http://www.postgresql.org/docs/9.4/static/sql-createsequence.html

序列对于并发写入是安全的。

CREATE TABLE contacts (
  contact_id      SERIAL PRIMARY KEY,
  user_id         INTEGER,
  user_contact_id SERIAL
);

INSERT INTO contacts (user_id) VALUES
  (1), (1), (1), (2), (2), (2), (3);

结果如下:

> SELECT * FROM contacts;
 contact_id | user_id | user_contact_id
------------+---------+-----------------
          1 |       1 |               1
          2 |       1 |               2
          3 |       1 |               3
          4 |       2 |               4
          5 |       2 |               5
          6 |       2 |               6
          7 |       3 |               7

> \d contacts
                                    Table "public.contacts"
     Column      |  Type   |                             Modifiers
-----------------+---------+--------------------------------------------------------------------
 contact_id      | integer | not null default nextval('contacts_contact_id_seq'::regclass)
 user_id         | integer |
 user_contact_id | integer | not null default nextval('contacts_user_contact_id_seq'::regclass)
Indexes:
    "contacts_pkey" PRIMARY KEY, btree (contact_id)

【讨论】:

我对原始问题的理解是,OP 希望 user_contact_id 具有每个 user_id 独立增加的值。 哦,我没注意到,抱歉,如果是这样,我就删除这个答案

以上是关于PostgreSQL 中的从属 SERIAL 列的主要内容,如果未能解决你的问题,请参考以下文章

使用 Hibernate 注释映射 PostgreSQL 串行类型

流复制postgresql从属未连接

尝试 PostgreSQL 主从 WAL 复制时出现“主 PostgreSQL 标识符与从属 PostgreSQL 标识符不匹配”错误

将列分组为一行,忽略 postgreSQL 中的空值

为啥这个查询这么慢? - PostgreSQL - 从 SERIAL、TIMESTAMP 和 NUMERIC(6,2) 中选择

Postgresql SERIAL 的工作方式是不是不同?