Предисловие: Это плохой дизайн, как уже отмечали другие.
Допущения:
create table a (a_id number primary key);
create table b (b_id number primary key);
create table v1
(v1_id number primary key, a_id number references a, b_id number references b);
create table v2
(v2_id number primary key, a_id number references a, b_id number references b);
create table v3
(v3_id number primary key, a_id number references a, b_id number references b);
Обязательный в любом изV_i
таблицы, в которых требуется ровно один из идентификаторов из A или B (но не оба), довольно просты.
alter table V1
add constraint v1_check check
( (a_id is null and b_id is not null)
or (a_id is not null and b_id is null)
);
Если вы хотите расширить это ограничение, чтобы точно один из идентификаторов изA или B присутствует, и это значение существует в одной и только одной строке:
create unique index v1_check_unique on v1 ( coalesce (a_id, b_id) );
Сложная задача - убедиться, что идентификаторы из A и B существуют в одной и только одной из таблиц V_i
,Это не может быть сделано во время DML, но оно может быть применено во время фиксации.
create materialized view log on v1 with rowid;
create materialized view log on v2 with rowid;
create materialized view log on v3 with rowid;
CREATE MATERIALIZED VIEW CROSS_TABLE
REFRESH FAST ON COMMIT
AS
SELECT V1_ID AS V_ID, 'V1' AS TABLE_NAME, ROWID AS ROW_ID,
COALESCE (A_ID, B_ID) AS OTHER_ID FROM V1
UNION ALL
SELECT V2_ID AS V_ID, 'V2' AS TABLE_NAME, ROWID AS ROW_ID,
COALESCE (A_ID, B_ID) AS OTHER_ID FROM V2
UNION ALL
SELECT V3_ID AS V_ID, 'V3' AS TABLE_NAME, ROWID AS ROW_ID,
COALESCE (A_ID, B_ID) AS OTHER_ID FROM V3
/
ALTER TABLE CROSS_TABLE ADD CONSTRAINT CROSS_TABLE_UNIQUE UNIQUE (OTHER_ID);
Это работает, но не так круто, как вы надеетесь.Oracle не может применить эту уникальность для всех таблиц во время оператора времени, поскольку сеанс А не может учитывать любые другие изменения, которые могут вносить другие сеансы.Эта уникальность может быть применена только в commit time.
Следующий тестовый пример завершается неудачно при запуске с пустыми таблицами - и откатывает всю транзакцию, так как не может определить, что вызываетотказ.Будьте бдительны.
INSERT INTO A VALUES (1);
INSERT INTO B VALUES (1);
INSERT INTO V1 (V1_ID, A_ID, B_ID) VALUES (1, 1, NULL);
INSERT INTO V2 (V2_ID, A_ID, B_ID) VALUES (1, 1, NULL);
COMMIT;