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
I suggest you to create
latest_version
as a VIEW :Then you can reference the VIEW like a table in any kind of query :
see demo in dbfiddle
Create
latest_version
without a reference and make sure thatversion.id
is a foreign key referencinglatest_version
. You have an 1:n relation, that is, ONElatest_version
corresponds to 0 or more records inversion
, henceversion
is the table that needs to referencelatest_version
, because the foreign key you wanted to define cannot uniquely identify the record byversion.id
, because this value is duplicated across multiple records.