2013-03-20 78 views
3

假設我有以下結構(SQL Server語法):如何執行「ALL-TO-ALL」關係?

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

+1

似乎是一個重複:http://stackoverflow.com/questions/11183847/sql-many-to-many-combination-table – 2013-03-20 23:16:25

+1

我建議把你的表成[小提琴](HTTP:// sqlfiddle .com)並更新您的問題 – hd1 2013-03-20 23:17:25

回答

2

您需要先插入到表中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小提琴演示這裏(注:只有SQL小提琴只顯示錶C輸出;還錯誤演示已經因爲這樣的錯誤註釋掉整個事情,而不僅僅是一個錯誤行)。 http://sqlfiddle.com/#!3/34d2f/4

相關問題