Environment
I came up with a view and trigger based solution to modify rows in multiple underlying tables in a Postgres 9.5.3 database. This works reasonable using Qt's model view programming. The next task I have to solve is mapping n:m relations which refer to the mentioned objects.
Tables
CREATE TABLE art (
id bigserial NOT NULL PRIMARY KEY,
name character varying(40),
-- more columns, irrelevant to the problem
);
CREATE TABLE tag (
id bigserial NOT NULL PRIMARY KEY,
name character varying(40) NOT NULL UNIQUE,
useable boolean NOT NULL DEFAULT false
);
CREATE TABLE art_tag (
article_ref bigint NOT NULL,
tag_ref bigint NOT NULL,
CONSTRAINT art_tag_pk PRIMARY KEY (article_ref, tag_ref),
CONSTRAINT art_tag_article_fk FOREIGN KEY (article_ref)
REFERENCES art (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT art_tag_tag_fk FOREIGN KEY (tag_ref)
REFERENCES tag (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE
);
The view is populated with every relevant column of the main table art (articles) and another column with all tag IDs as array - aggregated with array_agg(<column>) AS <alias>.
Whenever I edit a view row I have to handle the array which resolves to the surrogate key table.
Required Operation
I was looking for a kind of array subtraction which can be explained by the following examples.
{1,2,4} - {1,2,3} = {4}
{1,2,3} - {1,2,4} = {3}
Solution
This code could be used inside the triggers (remove the clutter beforehand) to calculate all elements which have to be deleted from or inserted into the surrogate key table.
DO
$do$
DECLARE
n bigint[] := array[1,2,3];
o bigint[] := array[1,2,4];
i integer;
BEGIN
FOREACH i IN ARRAY n
LOOP
IF NOT (SELECT i = ANY (o)) THEN
RAISE NOTICE 'insert: %', i; -- execute insert query
END IF;
END LOOP;
FOREACH i IN ARRAY o
LOOP
IF NOT (SELECT i = ANY (n)) THEN
RAISE NOTICE 'delete: %', i; -- execute delete query
END IF;
END LOOP;
END
$do$
resulting in:
NOTICE: insert: 3
NOTICE: delete: 4
which perfectly fits the expected output. Replacing the NOTICE with a suitable query should suffice. As the trigger always handles exactly one view row the second referenced id, not contained in the array, is known to the trigger function. So following the example code I got two pairs
(1,3) and (1,4)
where 1 represents the article id. The first pair is the one to insert into the surrogate key table and the second one is the one to delete.
Question
Will that just work or is it even an appropriate solution to the task? Is there any other, maybe better way to achieve this? (Concurrency should be assured.)
Qt's model view programming works pretty simple if exactly one table is affected. Otherwise I have to code more stuff. The chosen solution suites me because I like to put as much as possible into the database.