Какой лучший способ обойти уникальные ограничения в Postgres? - PullRequest
0 голосов
/ 18 марта 2019

В Postgres я пытаюсь унаследовать уникальный атрибут от родителя до его потомков. Родительская таблица является абстрактной таблицей, в которой нет записей. Имена всех детей должны быть уникальными. Обращаясь к следующему небольшому (глупому) примеру: не должно быть яблока с таким же именем, как у банана (или, конечно, другого яблока).

Минималистичный пример Postgres для этого сценария:

CREATE TABLE fruit(CONSTRAINT fruit_uniq_name UNIQUE (name))
CREATE TABLE banana(name text, length integer) INHERITS (fruit)
CREATE TABLE apple(name, diameter integer NOT NULL,) INHERITS (fruit)

Прочитав много постов по этой проблеме. Все они пришли к выводу, что этот сценарий невозможно освоить только с наследством Postgres, я хотел бы знать, есть ли обходной путь передовой практики, например, используя триггеры и функции, для этой проблемы?

Я был бы очень рад за каждый небольшой фрагмент кода, который мог бы помочь мне выбраться из этой надоедливой ловушки.

1 Ответ

0 голосов
/ 23 марта 2019

Я последовал совету Лоренца Альбе и думаю, что решил проблему, используя триггеры на столах яблок и бананов и триггерную функцию tgf_name_exists(), которая проверяет уникальность.

Это триггерная функция, которая проверяет уникальность имен детей:

CREATE OR REPLACE FUNCTION tgf_name_exits()
    RETURNS trigger
    LANGUAGE 'plpgsql'
    VOLATILE
    COST 100
AS $BODY$
    declare
    count_apple integer;
    count_banana integer;
    name text;
    schema text;
    error_text text;
BEGIN
    -- Variables
    error_text = '';
    schema = TG_TABLE_SCHEMA; -- actual schema
    name = NEW.name; --- actual attribute name

    -- Check
    EXECUTE format('SELECT count(*) FROM %s.apple apl WHERE apl.name=%L', schema, name) INTO count_apple;
    EXECUTE format('SELECT count(*) FROM %s.banana ban WHERE ban.name=%L', schema, name) INTO count_banana;

    -- Info 
    RAISE NOTICE 'Schema: %', schema;
    RAISE NOTICE 'Name: %', name;
    RAISE NOTICE 'Count: %', count_apple;
    RAISE NOTICE 'Count: %', count_banana;

    IF count_apple > 0 OR count_banana > 0 THEN
        -- Name ist already used
        if count_apple > 0 then
            error_text = error_text || "apple "
        end if;
        if count_banana > 0 then
            error_text = error_text || "banana "
        end if;
        RAISE EXCEPTION 'Name % already existing in table %', name, error_text;
    ELSE
        -- Name is unused -> OK 
        RETURN NEW;
    END IF;
END;
$BODY$;

Это триггеры для столов яблоко и банан

CREATE TRIGGER tg_apple_name_instert_update
    BEFORE INSERT OR UPDATE 
    ON apple
    FOR EACH ROW
    EXECUTE PROCEDURE tgf_name_exits();

CREATE TRIGGER tg_banana_name_uniq
    BEFORE INSERT OR UPDATE 
    ON banana
    FOR EACH ROW
    EXECUTE PROCEDURE tgf_name_exits();

Было бы очень любезно, если бы кто-нибудь смог это проверить. Отсюда похоже, что это работает.

...