条件唯一约束,忽略相同的外键
Conditional Unique Constraint, Ignored for Same Foreign Key
我有一种情况需要有条件地为两列 (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 是新的。最后,由于对 (FK_1,NAME).
的唯一约束的正常功能,第 5 行很好
它正在尝试确定如何处理复杂的第 3 行。
我见过几个例子,说明当 FK_2 的等价物在某种程度上被硬编码 (Oracle: function based index selective uniqueness) 时如何解决同样的问题,但我不确定我是如何解决的可以为外键解决这个问题,我真的无法控制 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
这个例外很有意义,但是无法查询 table 让我不知所措。
一个有趣的问题。我会通过以下方式解决这个问题:
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;
/
Table t123
是包含我们数据的主要 table。
Table 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');
我有一种情况需要有条件地为两列 (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 是新的。最后,由于对 (FK_1,NAME).
的唯一约束的正常功能,第 5 行很好它正在尝试确定如何处理复杂的第 3 行。
我见过几个例子,说明当 FK_2 的等价物在某种程度上被硬编码 (Oracle: function based index selective uniqueness) 时如何解决同样的问题,但我不确定我是如何解决的可以为外键解决这个问题,我真的无法控制 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
这个例外很有意义,但是无法查询 table 让我不知所措。
一个有趣的问题。我会通过以下方式解决这个问题:
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;
/
Table t123
是包含我们数据的主要 table。
Table 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');