假设我有以下结构(SQL Server 语法):
CREATE TABLE A (
key_A int NOT NULL PRIMARY KEY CLUSTERED,
info_A nvarchar(50) NULL
);
CREATE TABLE B(
key_B int NOT NULL PRIMARY KEY CLUSTERED,
info_B nvarchar(50) NULL
);
CREATE TABLE C(
key_C int NOT NULL PRIMARY KEY CLUSTERED,
key_A int NOT NULL,
key_B int NOT NULL,
info1 nvarchar(50) NULL,
info2 nvarchar(50) NULL,
info3 nvarchar(50) NULL
);
ALTER TABLE C WITH CHECK ADD CONSTRAINT FK_C_A FOREIGN KEY(key_A) REFERENCES A (key_A);
ALTER TABLE C WITH CHECK ADD CONSTRAINT FK_C_B FOREIGN KEY(key_B) REFERENCES B (key_B);
因此,表 C 有两个主键指向表 A 和表 B。 表 C 必须具有表 A 和表 B 的笛卡尔积。这意味着所有组合,因此当在表 A 中插入新记录时,我们必须在表 C 中插入几行,所有行都在 A 中进行新引用在 B 中,反之亦然,在 B 中插入的情况下。
问题是,在 SQL Server 中,表 C 必须具有 A 和 B 的所有组合,您如何确保这种关系的完整性?或者,如果您认为这样的结构是一种不好的做法,您推荐哪些替代表不会增加必须执行 DISTINCT 选择等的麻烦?
谢谢!
fiddle 链接: Fiddle
最佳答案
您需要先插入表 A/B,然后才能在表 C 中引用这个新条目。我知道的唯一方法是在表 A 和 B 上创建一个触发器,以便在新条目被插入时填充表 C对这些表中的任何一个进行。问题是你在其他领域投入了什么?由于这些是可为空的,我假设您很乐意将它们默认为空?如果不是(即您希望用户输入有效值),唯一的方法是在您的应用程序的逻辑中,而不是在数据库级别(或者通过使用存储过程来填充这些过程具有合适逻辑的表)除了 A/B 之外,在 C 中创建适当的条目)。
触发代码示例:
use StackOverflowDemos
go
if OBJECT_ID('TRG_C_DELETE','TR') is not null drop trigger TRG_C_DELETE
if OBJECT_ID('TRG_A_INSERT','TR') is not null drop trigger TRG_A_INSERT
if OBJECT_ID('TRG_B_INSERT','TR') is not null drop trigger TRG_B_INSERT
if OBJECT_ID('C','U') is not null drop table C
if OBJECT_ID('A','U') is not null drop table A
if OBJECT_ID('B','U') is not null drop table B
go
CREATE TABLE A
(
key_A int NOT NULL IDENTITY(1,1) CONSTRAINT PK_A PRIMARY KEY CLUSTERED,
info_A nvarchar(50) NULL
);
go
CREATE TABLE B
(
key_B int NOT NULL IDENTITY(1,1) CONSTRAINT PK_B PRIMARY KEY CLUSTERED,
info_B nvarchar(50) NULL
);
go
CREATE TABLE C
(
key_C int NOT NULL IDENTITY(1,1) CONSTRAINT PK_C PRIMARY KEY CLUSTERED,
key_A int NOT NULL CONSTRAINT FK_C_A FOREIGN KEY(key_A) REFERENCES A (key_A),
key_B int NOT NULL CONSTRAINT FK_C_B FOREIGN KEY(key_B) REFERENCES B (key_B),
info1 nvarchar(50) NULL,
info2 nvarchar(50) NULL,
info3 nvarchar(50) NULL
);
go
CREATE TRIGGER TRG_A_INSERT
ON A
AFTER INSERT
AS
BEGIN
--add new As to C
insert C (key_A, key_B)
select key_A, key_B
from inserted
cross join B
END;
go
CREATE TRIGGER TRG_B_INSERT
ON B
AFTER INSERT
AS
BEGIN
--add new As to C
insert C (key_A, key_B)
select key_A, key_B
from inserted
cross join A
END;
go
CREATE TRIGGER TRG_C_DELETE
ON C
AFTER DELETE
AS
BEGIN
DELETE
FROM B
WHERE key_B IN
(
SELECT key_B
FROM DELETED d
--ths row onwards are here to cover the circumstance that the record being deleted isn't the only instance of B in this table
WHERE key_B NOT IN
(
SELECT key_B
FROM C
WHERE C.key_C NOT IN
(
SELECT key_C
FROM deleted
)
)
)
DELETE
FROM A
WHERE key_A IN
(
SELECT key_A
FROM DELETED d
--ths row onwards are here to cover the circumstance that the record being deleted isn't the only instance of A in this table
WHERE key_A NOT IN
(
SELECT key_A
FROM C
WHERE C.key_C NOT IN
(
SELECT key_C
FROM deleted
)
)
)
END;
go
insert A select 'X'
select * from C --no results as no Bs yet
insert A select 'Y'
select * from C --no results as no Bs yet
insert B select '1'
select * from C --2 results; (X,1) and (Y,1)
insert A select 'Z'
select * from C --3 results; the above and (Z,1)
delete from A where info_A = 'Y'
select * from C --3 results; as above since the previous statement should fail due to enforced referential integrity
insert C (key_A, key_B, info1)
select a.key_A, b.key_B, 'second entry for (Y,1)'
from A
cross join B
where a.info_A = 'Y'
and b.info_B = '1'
select * from C --4 results; as above but with a second (Y,1), this time with data in info1
delete from C where info1 = 'second entry for (Y,1)'
select * from C --3 results; as above but without the new(Y,1)
select * from A --3 results
select * from B --1 result
delete from C where key_A in (select key_A from A where info_A = 'Y')
select * from C --2 results; (X,1) and (Z,1)
select * from A --2 results; X and Z
select * from B --1 result
delete from C where key_B in (select key_B from B where info_B = '1')
select * from C --0 results
select * from A --0 results
select * from B --0 result
SQL Fiddle 演示在这里(注意:只有 SQL Fiddle 只显示表 C 的输出;错误演示也已被注释掉,因为这会导致整个错误,而不仅仅是一个错误行)。 http://sqlfiddle.com/#!3/34d2f/4
关于sql - 如何强制执行 "ALL-TO-ALL"关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15536529/