An alternative solution to this problem is using array columns instead of rows and the INSERT INTO ... ON CONFLICT UPDATE ...
support.
Online example: https://www.db-fiddle.com/f/2y46V6EEVJLQ5cPNTDAUPy/0
Structure:
CREATE TABLE test_rr (
id serial primary key,
fk_1 integer not null,
fk_2 integer not null,
latest timestamptz[] not null
);
CREATE UNIQUE INDEX idx_unique_rr ON test_rr (fk_1, fk_2);
Upserting data:
INSERT INTO test_rr (fk_1, fk_2, latest)
VALUES (1, 2, array[current_timestamp])
ON CONFLICT (fk_1, fk_2) DO UPDATE SET latest = (array_cat(EXCLUDED.latest, test_rr.latest))[:10];
Selecting entries:
SELECT id, fk_1, fk_2, unnest(latest) AS ts FROM test_rr WHERE fK_1 = 1 AND fk_2 = 2;
...resulting into:
id | fk_1 | fk_2 | ts
-----+------+------+-------------------------------
652 | 1 | 2 | 2019-03-10 13:28:57.806489+01
652 | 1 | 2 | 2019-03-10 13:28:56.670678+01
652 | 1 | 2 | 2019-03-10 13:28:55.470668+01
652 | 1 | 2 | 2019-03-10 13:28:54.174111+01
652 | 1 | 2 | 2019-03-10 13:28:52.878719+01
652 | 1 | 2 | 2019-03-10 13:28:51.3748+01
652 | 1 | 2 | 2019-03-10 13:28:49.886457+01
652 | 1 | 2 | 2019-03-10 13:28:48.190317+01
652 | 1 | 2 | 2019-03-10 13:28:46.350833+01
652 | 1 | 2 | 2019-03-10 13:11:50.506323+01
(10 rows)
Instead of timestamptz[]
, you can also create your own type to support more columns:
CREATE TYPE my_entry_data AS (ts timestamptz, data varchar);
CREATE TABLE test_rr (
id serial primary key,
fk_1 integer not null,
fk_2 integer not null,
latest my_entry_data[] not null
);
CREATE UNIQUE INDEX idx_unique_rr ON test_rr (fk_1, fk_2);
-- ...
INSERT INTO test_rr (fk_1, fk_2, latest)
VALUES (1, 2, array[(current_timestamp,'L')::my_entry_data])
ON CONFLICT (fk_1, fk_2) DO UPDATE
SET latest = (array_cat(EXCLUDED.latest, test_rr.latest))[:10];
SELECT id, fk_1, fk_2, tmp.ts, tmp.data
FROM test_rr, unnest(latest) AS tmp -- LATERAL function call
WHERE fK_1 = 1 AND fk_2 = 2;
However, load tests have to show whether this is actually faster than triggers or other approaches. At least this has the benefit that rows will just be updated and not inserted + deleted which might save some I/O.