Home > Software design >  Is is possible to create two tables with disjoint identifiers?
Is is possible to create two tables with disjoint identifiers?

Time:09-25

By "disjoint" I mean mutually exclusive sets of ID values. No overlap between both tables.
For example, the sequence generator for the id column on both tables should work in conjunction to make sure they are always disjoint. I am not sure if this is possible. So, I thought I would just ask here.

Table A

id name
0 abc
1 cad
2 pad
3 ial

Table B

id name
40 pal
50 sal

CodePudding user response:

A very simple way is to share the same 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<>fiddle here

This way, table a "owns" the sequence, while table b draws from the same source. You can also create an independent SEQUENCE if you prefer.

Note: this only guarantees mutually exclusive new IDs (even under concurrent write load) while you don't override default values and also don't update them later.

Related:

CodePudding user response:

Welcome to the painful world of inter-table constraints or assertions - this is something that ISO SQL and pretty much every RDBMS out there does not handle ergonomically...

(While ISO SQL does describe both deferred-constraints and database-wide assertions, as far as I know only PostgreSQL implements deferred-constraints, and no production-quality RDBMS supports database-wide assertions).


One approach is to have a third-table which is the only table with SERIAL (aka IDENTITY aka AUTO_INCREMENT) with a discriminator column which combined forms the table's primary-key, then the other two tables have an FK constraint to that PK - but they'll also need the same discriminator column (enforced with a CHECK constraint), but you will never need to reference that column in most queries.

As your post doesn't tell us what the real table-names are, I'll use my own.

Something like this:

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 )
)

With this design, it is impossible for any row in shitposts to share a postId value with a post in goldposts and vice-versa.

However it is possible for a row to exist in postIds without having any row in both goldposts and shitposts. Fortunately, as you are using PostgreSQL you could add a new FK constraint from postIds to both goldposts and shitposts but use it with deferred-constraints.

  • Related