skip to Main Content

I like to store my current data along with historic data. I did the following:

CREATE TABLE version (
  sn   serial PRIMARY KEY,
  id   int    NOT NULL,
  del  timestamp,
  name text,
  UNIQUE NULLS NOT DISTINCT (id, del)
);

sn is the key selecting both current and historic data. id is the key selecting only the current data. This works because NULLS NOT DISTINCT allows only one NULL value. So the row where del is NULL is the current value.

I can create rows:

INSERT INTO version (id, name) VALUES (1, 'first');

And update them by marking the old as deleted and inserting an additional row:

UPDATE version SET del = now()::timestamp WHERE id = 1 AND del IS NULL;
INSERT INTO version (id, name) VALUES (1, 'second');

This works fine:

 sn | id |            del            |  name  
----+----+---------------------------+--------
  1 |  1 | 2024-05-27 12:57:16.34936 | first
  2 |  1 |                           | second

But referencing is a bit awkward.

CREATE TABLE latest_version(
  id  int       NOT NULL,
  del timestamp CHECK (del IS NULL),
  FOREIGN KEY (id, del) REFERENCES version(id, del)
);

It is necessary to maintain an empty del column. Is there any way to get rid of this useless column?

I tried this, but it does not work:

DROP TABLE IF EXISTS latest_version CASCADE;
CREATE TABLE latest_version(
  id int NOT NULL,
  FOREIGN KEY (id, NULL) REFERENCES version(id, del)
);

Is there any other way to restrict the foreign key to those rows where del is NULL?

Update

This does not work either:

CREATE TABLE version (
  sn   serial PRIMARY KEY,
  id   int    NOT NULL,
  del  timestamp,
  name text
);
CREATE UNIQUE INDEX version_id_key ON version (id) WHERE del IS NULL;
CREATE TABLE latest_version(
  id int  NOT NULL REFERENCES version(id)
);

ERROR: there is no unique constraint matching given keys for referenced table "version"

I still do not understand this message, because there is an unique index:

                                      Table "public.version"
 Column |            Type             | Collation | Nullable |               Default               
--------+-----------------------------+-----------+----------+-------------------------------------
 sn     | integer                     |           | not null | nextval('version_sn_seq'::regclass)
 id     | integer                     |           | not null | 
 del    | timestamp without time zone |           |          | 
 name   | text                        |           |          | 
Indexes:
    "version_pkey" PRIMARY KEY, btree (sn)
    "version_id_key" UNIQUE, btree (id) WHERE del IS NULL

2

Answers


  1. I suggest you to create latest_version as a VIEW :

    CREATE VIEW latest_version AS
    (SELECT * FROM version WHERE del IS NULL) ;
    

    Then you can reference the VIEW like a table in any kind of query :

    SELECT * FROM latest_version
    

    see demo in dbfiddle

    Login or Signup to reply.
  2. Create latest_version without a reference and make sure that version.id is a foreign key referencing latest_version. You have an 1:n relation, that is, ONE latest_version corresponds to 0 or more records in version, hence version is the table that needs to reference latest_version, because the foreign key you wanted to define cannot uniquely identify the record by version.id, because this value is duplicated across multiple records.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search