Условное уникальное ограничение, игнорируемое для того же внешнего ключа - PullRequest
1 голос
/ 15 марта 2019

У меня есть ситуация, когда мне нужно условно поддерживать уникальное ограничение для двух столбцов (FK_1, NAME). Ограничение не должно применяться к строкам, в которых второй внешний ключ одинаков (FK_2).

ID    FK_1     FK_2    NAME
1     2        3       'X01'
2     2        3       'X01-A'
3     2        3       'X01'  --Accepted
4     2        4       'X01'  --Violation
5     3        5       'X01'  --Accepted

Я пытаюсь сделать так, чтобы строки 1, 2 и 3 были действительными, потому что FK_2 одинаков. Они нарушают ограничение уникальности, но ограничение уникальности игнорируется, потому что FK_2 то же самое. Строка 4 будет нарушать ограничение уникальности для (FK_1, NAME), поскольку FK_2 был бы новым. И, наконец, строка 5 в порядке благодаря нормальной функции ограничения уникальности (FK_1, NAME).

Он пытается определить, как обрабатывать строку 3, что является осложнением.

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

Я попытался реализовать уникальный индекс на основе функций, но это приводит к исключению ORA-04091.

CREATE OR REPLACE FUNCTION UNQ_TEST_FUNCTION(var_fk_1 IN NUMBER, var_fk_2 IN NUMBER, var_name IN VARCHAR2) RETURN NUMBER DETERMINISTIC IS
    result          NUMBER;
BEGIN
    result := null;
    IF (var_fk_1 IS NULL OR var_fk_2 IS NULL OR var_name IS NULL ) THEN
        result := null;
    ELSE
        SELECT COUNT(ID) INTO result FROM TEST1 WHERE
            FK_2 != var_fk_2 AND FK_1 = var_fk_1 AND NAME = var_name;
        IF (result <= 0) THEN
            result := null;
        ELSE
            result := 1;
        END IF;
    END IF;

    RETURN result;
END;

CREATE UNIQUE INDEX UNQ_TEST ON TEST1 
    (
        UNQ_TEST_FUNCTION("FK_1","FK_2","NAME")
    ) 
;

INSERT INTO TEST1 (ID, FK_1, FK_2, NAME)
    VALUES (1, 2, 3, 'X01');

ORA-04091: table TEST1 is mutating, trigger/function may not see it

Исключение имеет смысл, но неспособность запросить таблицу затрудняет мне поиск.

1 Ответ

1 голос
/ 15 марта 2019

интересная проблема.Я бы подошел к этому следующим образом:

CREATE TABLE t123_fk(
  FK_1 int,
  FK_2 int,
  CONSTRAINT t123_fk_pk PRIMARY KEY(FK_1, FK_2),
  CONSTRAINT fk_2_is_new_constr_violated UNIQUE(FK_1)
);

CREATE TABLE t123(
  ID int,
  FK_1 int,
  FK_2 int,
  NAME varchar2(100),
  constraint t123_fk FOREIGN KEY(FK_1, FK_2) REFERENCES t123_fk
);

CREATE OR REPLACE TRIGGER some_name 
BEFORE INSERT OR UPDATE ON t123
FOR EACH ROW
BEGIN
  INSERT INTO t123_fk( fk_1, fk_2 )
  SELECT :new.FK_1, :new.FK_2 FROM dual
  WHERE NOT EXISTS(
     SELECT 1 FROM t123_fk
     WHERE fk_1 = :new.fk_1 AND fk_2 = :new.fk_2
  );
END;
/

Таблица t123 - это главная таблица, содержащая наши данные.
Таблица t123_fk и триггер являются вспомогательными и используются только для того, чтобы помочь нам форсировать наше ограничение (кстати, наше ограничение называется fk_2_is_new_constr_violated в приведенном выше коде).


Здесьэто тест - четвертая вставка будет отклонена базой данных:

insert into t123( id, fk_1, fk_2, name) values(1,2,3,'X01');
insert into t123( id, fk_1, fk_2, name) values(2,2,3,'X01-A');
insert into t123( id, fk_1, fk_2, name) values(3,2,3,'X01');
insert into t123( id, fk_1, fk_2, name) values(4,2,4,'X01'); // this insert will be rejected
insert into t123( id, fk_1, fk_2, name) values(5,3,5,'X01');
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...