3

次の構造 (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 に対する 2 つの主キーがあります。テーブル C には、テーブル A とテーブル B のデカルト積が必要です。つまり、すべての組み合わせを意味するため、テーブル A に新しいレコードが挿入されるときは、挿入する必要があります。テーブル C では、B のすべての行による A の新しい参照を含む複数の行。B への挿入の場合はその逆です。

問題は、テーブル C が A と B のすべての組み合わせを持つ必要がある SQL Server で、そのような関係の整合性をどのように強制できるかということです。または、そのような構造を悪い習慣だと考える場合、DISTINCT 選択などを行う手間がかからない代替テーブルをお勧めしますか?

ありがとう!

フィドルへのリンク: フィドル

4

1 に答える 1

2

テーブル C でこの新しいエントリを参照するには、テーブル A / B に挿入する必要があります。それらのテーブルの。問題は、他のフィールドに何を入れるかです。これらはnull可能であるため、デフォルトでnullに設定しても問題ないと思いますか? そうでない場合 (つまり、ユーザーに有効な値を入力させたい場合)、これを行う唯一の方法は、データベース レベルではなく、アプリケーションのロジックで行うことです (または、ストアド プロシージャを使用して、これらのプロシージャが適切なロジックを持つテーブルにデータを入力することによって)。 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 からの出力のみを表示します。エラー デモは、1 つのエラー行だけではなく全体がエラーになるため、コメント アウトされています)。 http://sqlfiddle.com/#!3/34d2f/4

于 2013-03-20T23:26:24.477 に答える