Khóa ngoại với một hằng


11

Giả sử tôi có một bảng A, có hai cột: một là ID cho ThingAvà một là ID cho ThingB. Chìa khóa chính là (ThingA, ThingB).

Tiếp theo, tôi có một bảng thứ hai, nhưng lần này nó bị giới hạn ở các mục trong bảng AThingB = 3. Khóa chính là ThingA, bởi vì ThingBhằng số là 3.

Ban đầu, tôi đã nghĩ rằng tôi có thể đơn giản:

FOREIGN KEY (ThingA, 3) REFERENCES A(ThingA, ThingB)

Nhưng tôi đã học được rằng đó không phải là trường hợp và tôi phải tạo một cột cho ThingB:

ThingB INT NOT NULL DEFAULT(3) CHECK(ThingB = 3)

Sau đó,

FOREIGN KEY (ThingA, ThingB) REFERENCES A (ThingA, ThingB)

Có một giải pháp thay thế nào không yêu cầu thêm cột hay DEFAULT + CHECKkhông? Một thay thế là một cột được tính toán bền bỉ, nhưng tôi ghét ý tưởng đó vì về cơ bản nó là một mánh gian lận và vẫn thêm một cột mới với lưu trữ vật lý. Mặc dù trên chính nó, INTsẽ không lớn, có vài triệu hàng cần nó trên một số bảng và tôi không muốn duy trì các cột bổ sung.

Dưới đây là mẫu DDL để minh họa tình huống:

CREATE TABLE Test1
(
    ThingA INT NOT NULL,
    ThingB INT NOT NULL,
    PRIMARY KEY (ThingA, ThingB)
);

CREATE TABLE Test2
(
    ThingAVal INT NOT NULL,
    ThingBVal INT NOT NULL DEFAULT(3) CHECK(ThingBVal = 3),
    Val INT NOT NULL,
    FOREIGN KEY (ThingAVal, ThingBVal) REFERENCES Test1 (ThingA, ThingB)
);

Và tôi đã tạo một db <> fiddle thể hiện giải pháp (hiện tại) của mình:

Nếu câu trả lời là "Không", tôi sẽ chấp nhận nó, nhưng tôi tò mò liệu có lựa chọn thay thế nào khác không .


1
Tôi sợ rằng không có giải pháp hoàn hảo nào trong việc triển khai các DBMS SQL hiện tại. Nhưng bạn có thể sử dụng TINYINT, nếu số lượng giá trị có thể ThingBlà nhỏ.
ypercubeᵀᴹ

@ ypercubeᵀᴹ Vâng, đó là một SMALLINTsản phẩm, tôi chỉ ước có một cách tốt hơn để liên kết nó. Tôi có thể loại bỏ Ahoàn toàn, vì tôi thực sự không cần nó, và chỉ thực hiện tính toàn vẹn tham chiếu thông qua ThingA, do đó cho phép tôi loại bỏ ThingB.
Der Kommissar

1
Nếu tôi có thời gian, tôi sẽ thêm câu trả lời sau với suy nghĩ của mình về điều này. Nếu không, vào cuối tuần. Tôi đã có một câu hỏi tương tự / liên quan, một số năm trước đây. Có DBMS nào cho phép Khóa ngoài tham chiếu Chế độ xem (và không chỉ các bảng cơ sở) không?
ypercubeᵀᴹ

@ ypercubeᵀᴹ Tôi đánh giá cao nó. Gãi đầu ở đây, và các giải pháp của tôi chỉ trở nên phức tạp hơn.
Der Kommissar

Có lý do nào khiến bạn chọn không sử dụng khóa dựa trên danh tính thay thế cùng với một UNIQUEràng buộc (ThingA, ThingB)không?
John Eisbrener

Câu trả lời:


0

Tôi nghĩ rằng sự kết hợp của khóa thay thế dbo.Test1kích hoạt được thực thi sau cả hai INSERTvà các UPDATEcâu lệnh, cụ thể là một trình INSTEAD OFkích hoạt, là câu trả lời ở đây.

Và có thể trông giống như thế này ( fiddle ):

Lược đồ

create table dbo.Test1 (
    Id int identity primary key,
    ThingA int not null,
    ThingB int not null
);

create table dbo.Test2 (
    Id int identity primary key,
    Test1Id int not null foreign key references Test1 (Id),
    Val int not null
);

Sau khi INSERTkích hoạt

create trigger test2ThingBCheck_Insert 
on dbo.Test2  
instead of INSERT 
as  
begin

    insert into dbo.Test2 (Test1Id, Val)
    select
        t.Id
        ,i.Val
    from 
        Test1 t 
    join 
        inserted i 
        on i.Test1Id = t.Id
    where
        t.ThingB = 3;
end;

Sau khi UPDATEkích hoạt

create trigger test2ThingBCheck_Update
on dbo.Test2  
instead of Update 
as  
begin
    update
        t2
    set
        Val = i.Val
    from
        dbo.Test2 t2
    join
        inserted i
        on i.Id = t2.Id        
    join
        dbo.Test1 t1
        on t1.Id = i.Test1Id
    where
        t1.ThingB = 3;
end; 

0

Nếu bạn biết giá trị không đổi của ThingB, thì tôi khuyên bạn nên bỏ cột "ThingB" đi. Thay vào đó hãy để logic kinh doanh thêm giá trị không đổi. Giá trị chính xác là gì có thể được lưu trữ trong một bảng khác hoặc trong một số cài đặt.


Sau đó, không có tính toàn vẹn tham chiếu cho ThingA
Lennart

0

Bạn nói rằng bạn không muốn thêm một cột phụ vào một số bảng cần liên kết lại với Test1 theo cách này (tức là trên ThingA, 3).

Làm thế nào về việc thêm một cột được tính toán bền vững vào TestA cho thấy giá trị ThingA nếu ThingB là 3 và null thì sao?

Sau đó, khóa ngoại của bạn chỉ tham chiếu cột mới, dựa trên ThingA trong bảng tham chiếu.

alter table Test1 add SpecialThingA as
    (case ThingB when 3 then ThingA else null end) persisted;

FOREIGN KEY (ThingA) REFERENCES Test1 (SpecialThingA)

Nói cách khác - một cột mới trên Test1, thay vì một cột mới trên Test2 (để giữ '3') và Test3, và ....


Không làm việc cho tôi trên máy chủ SQL vì cột vẫn tồn tại trong 'nchar' và cột mà tôi đang cố gắng tạo mối quan hệ là 'int'
juanora

@juanora - nếu cột 'ThingA' của bạn là số nguyên, thì hãy cập nhật câu lệnh tình huống thành: (case ThingB when 3 then cast(ThingA as int) else null end) persisted;- điều này sẽ đảm bảo cột được tính được xác định là một int.
youcantryreachingme

0

Tôi nghĩ rằng câu trả lời là không.

Khóa ngoại phải tham chiếu CONSTRAINT ĐỘC ĐÁO trong bảng được tham chiếu theo Sách trực tuyến

Có vẻ như giải pháp có thể là một chỉ mục duy nhất được lọc, nhưng điều đó không được tính là một ràng buộc.

Bạn có thể kiểm tra nó ở đây:

SELECT 
    i.name
    , i.is_primary_key          -- can be referenced by FK
    , i.is_unique_constraint    -- can be referenced by FK
    , i.is_unique               -- cannot be referenced by FK
FROM sys.indexes AS i 

Tôi không nghĩ bạn có thể làm điều đó một cách an toàn mà không cần sao chép dữ liệu - giải pháp của bạn có vẻ tốt nhất (KIỂM TRA ràng buộc trên bảng tham chiếu)

Chế độ xem được lập chỉ mục dựa trên Test1 với bộ lọc trên ThingB = 3 có thể cũng hoạt động, nhưng bạn sẽ duy trì một cột khác.

Tôi thường tránh các yếu tố kích hoạt vì chúng không thực thi tính toàn vẹn tham chiếu cũng như các ràng buộc khác, nhưng nó có thể là con đường để đi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.