【问题标题】:Is it possible to create two tables with disjoint identifiers?是否可以创建两个具有不相交标识符的表?
【发布时间】:2021-11-18 03:33:19
【问题描述】:

“不相交”是指互斥的 ID 值集。两个表之间没有重叠。
例如,两个表上id 列的序列生成器应该协同工作以确保它们始终是不相交的。我不确定这是否可能。所以,我想我会在这里问。

表 A

id name
0 abc
1 cad
2 pad
3 ial

表 B

id name
40 pal
50 sal

【问题讨论】:

  • 所以你在两个表中都使用了SERIAL
  • 没错。
  • 那你不能那样做,因为SERIAL就是这样工作的。您可以在多大程度上重新设计数据库?如果这些值来自 两个单独的 SERIAL 列,那么您无法阻止它们可能具有相同的值。但是,有一个解决方案需要使用第三个表。
  • 我想我可以容纳第三张桌子。该解决方案会是什么样子?
  • 唯一干净的解决方案是:重塑您的数据。这可能意味着第三张桌子。这个丑陋的问题不应该存在。

标签: sql postgresql database-design primary-key create-table


【解决方案1】:

另一种破解方法:


CREATE TABLE odd(
        id INTEGER GENERATED ALWAYS AS IDENTITY (START 1 INCREMENT 2)
        , val integer
        );


CREATE TABLE even(
        id INTEGER GENERATED ALWAYS AS IDENTITY (START 2 INCREMENT 2)
        , val integer
        );

INSERT INTO odd (val)
SELECT GENERATE_SERIES(1,10);

INSERT INTO even (val)
SELECT GENERATE_SERIES(1,20);

SELECT * FROM odd;
SELECT * FROM even;

结果:


CREATE TABLE
CREATE TABLE
INSERT 0 10
INSERT 0 20
 id | val 
----+-----
  1 |   1
  3 |   2
  5 |   3
  7 |   4
  9 |   5
 11 |   6
 13 |   7
 15 |   8
 17 |   9
 19 |  10
(10 rows)

 id | val 
----+-----
  2 |   1
  4 |   2
  6 |   3
  8 |   4
 10 |   5
 12 |   6
 14 |   7
 16 |   8
 18 |   9
 20 |  10
 22 |  11
 24 |  12
 26 |  13
 28 |  14
 30 |  15
 32 |  16
 34 |  17
 36 |  18
 38 |  19
 40 |  20
(20 rows)

【讨论】:

  • 聪明!您可以通过添加CHECK 约束来确保数字分别为MOD 2MOD 2 + 1,从而使其具有弹性。
【解决方案2】:

一个很简单的方法是共享同一个SEQUENCE

CREATE TABLE a (
  id serial PRIMARY KEY
, name text
);

CREATE TABLE b (
  id int PRIMARY KEY
, name text
);

SELECT pg_get_serial_sequence('a', 'id');  -- 'public.a_id_seq'

ALTER TABLE b ALTER COLUMN id SET DEFAULT nextval('public.a_id_seq');  -- !

db小提琴here

这样,a 表“拥有”序列,而b 表从同一源中提取。如果您愿意,也可以创建一个独立的SEQUENCE

注意:保证互斥的新 ID(即使在并发写入负载下),同时您不会覆盖默认值,也不会在以后更新它们。

相关:

【讨论】:

    【解决方案3】:

    欢迎来到表间约束或断言的痛苦世界 - 这是 ISO SQL 和几乎每个 RDBMS 都无法以符合人体工程学的方式处理的东西......

    (虽然 ISO SQL 确实描述了延迟约束和数据库范围的断言,据我所知只有 PostgreSQL 实现了延迟约束,并且没有生产质量的 RDBMS 支持数据库范围的断言) .


    一种方法是使用第三个表,它是 only 表,带有SERIAL(又名IDENTITY 又名AUTO_INCREMENT)和一个鉴别器列,该鉴别器列组合形成表的主键,那么其他两个表对该 PK 有一个 FK 约束 - 但它们也需要相同的鉴别器列(通过 CHECK 约束强制执行),但您永远不需要在大多数查询中引用该列。

    由于您的帖子没有告诉我们真正的表名是什么,我将使用我自己的。

    类似这样的:

    CREATE TABLE postIds (
        postId   int     NOT NULL SERIAL,
        postType char(1) NOT NULL, /* This is the discriminator column. It can only contain ONLY either 'S' or 'G' which indicates which table contains the rest of the data */
    
        CONSTRAINT PK_postIds PRIMARY KEY ( postId, postType ),
        CONSTRAINT CK_type CHECK ( postType IN ( 'S', 'G' ) )
    );
    
    CREATE TABLE shitposts (
        postId   int     NOT NULL,
        postType char(1) DEFAULT('S'),
    
        foobar   nvarchar(255)     NULL,
        etc      int           NOT NULL,
    
        CONSTRAINT PK_shitpostIds PRIMARY KEY ( postId, postType ),
        CONSTRAINT CK_type CHECK ( postType = 'S' ),
        CONSTRAINT FK_shitpost_ids FOREIGN KEY ( postId, postType ) REFERENCES postIds ( postId, postType )
    );
    
    CREATE TABLE goldposts (
        postId   int     NOT NULL,
        postType char(1) DEFAULT('G'),
    
        foobar   nvarchar(255)     NULL,
        etc      int           NOT NULL,
    
        CONSTRAINT PK_goldpostIds PRIMARY KEY ( postId, postType ),
        CONSTRAINT CK_type CHECK ( postType = 'G' ),
        CONSTRAINT FK_goldpost_ids FOREIGN KEY ( postId, postType ) REFERENCES postIds ( postId, postType )
    )
    

    使用这种设计,shitposts 中的任何行都不可能与goldposts 中的帖子共享postId 值,反之亦然。

    但是,postIds 中可能存在一行goldpostsshitposts 中没有任何行。幸运的是,当您使用 PostgreSQL 时,可以将新的 FK 约束从 postIds 添加到 goldpostsshitposts,但将其与延迟约束一起使用。

    【讨论】:

    • Postgres 中没有nvarchar。我会考虑"char"(带引号)而不是char(1)。 (永远不要使用char 类型)并将postType 设置为NOT NULL 或者可以使用默认MATCH SIMPLE 行为避开多列FK - 请参阅:dba.stackexchange.com/a/71508/3684。除此之外,这是一个很好(如果乏味)的解决方案!
    • @ErwinBrandstetter 是的,谢谢你指出这一点——我主要使用 T-SQL 而不是 Postgres,所以我的 T-SQLisms 漏掉了。
    • 旁白:SERIALIDENTITY 在 Postgres 中是相关但独立的概念。见:stackoverflow.com/a/9875517/939860
    • @ErwinBrandstetter 这个帖子说它们是相同的概念/功能,但 IDENTITY 符合 ISO 标准,SERIAL 是旧版:stackoverflow.com/questions/55300370/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-10
    • 1970-01-01
    • 2012-10-12
    相关资源
    最近更新 更多