Một cột null có thể là một phần của khóa chính không?


15

Tôi đang phát triển cơ sở dữ liệu SQL Server 2012 và tôi có một câu hỏi về mối quan hệ Một-Không-Một-Một.

Tôi có hai bảng, CodesHelperCodes. Một mã có thể có 0 hoặc một mã trợ giúp. Đây là tập lệnh sql để tạo hai bảng này và các mối quan hệ của chúng:

CREATE TABLE [dbo].[Code]
(
    [Id] NVARCHAR(20) NOT NULL, 
    [Level] TINYINT NOT NULL, 
    [CommissioningFlag] TINYINT NOT NULL, 
    [SentToRanger] BIT NOT NULL DEFAULT 0, 
    [LastChange] NVARCHAR(50) NOT NULL, 
    [UserName] NVARCHAR(50) NOT NULL, 
    [Source] NVARCHAR(50) NOT NULL, 
    [Reason] NVARCHAR(200) NULL, 
    [HelperCodeId] NVARCHAR(20) NULL,
    CONSTRAINT [PK_Code] PRIMARY KEY CLUSTERED
    (
        [Id] ASC
    ),
    CONSTRAINT [FK_Code_LevelConfiguration]
       FOREIGN KEY ([Level])
        REFERENCES [dbo].[LevelConfiguration] ([Level]),
    CONSTRAINT [FK_Code_HelperCode]
       FOREIGN KEY ([HelperCodeId])
        REFERENCES [dbo].[HelperCode] ([HelperCodeId])
)

CREATE TABLE [dbo].[HelperCode]
(
    [HelperCodeId] NVARCHAR(20) NOT NULL, 
    [Level] TINYINT NOT NULL, 
    [CommissioningFlag] TINYINT NOT NULL, 
    [LastChange] NVARCHAR(50) NOT NULL,
    CONSTRAINT [PK_HelperCode] PRIMARY KEY CLUSTERED
    (
        [HelperCodeId] ASC
    ),
    CONSTRAINT [FK_HelperCode_LevelConfiguration]
       FOREIGN KEY ([Level])
        REFERENCES [dbo].[LevelConfiguration] ([Level])
)

Đúng không?

Mã và HelperCode đều là các thực thể khác nhau. HelperCode có thể là một mã được sử dụng (không có Mã nào tham chiếu nó) hoặc được sử dụng (chỉ một Mã tham chiếu nó).

Có lẽ Code.HelperCodeId phải là một phần của khóa chính của bảng Code. Nhưng tôi không chắc chắn nếu một cột null có thể là một phần của chính. Làm điều này, tôi muốn ngăn chặn hai hoặc nhiều Mã tham chiếu cùng một HelperCode.


1
Tại sao bạn muốn HelperCodeIdtrở thành một phần của PK? Có phải, trong bất kỳ trường hợp nào, bởi vì bạn muốn ngăn hai hoặc nhiều Mã để tham chiếu cùng một HelperCode?
Andriy M

Có, tôi muốn ngăn hai hoặc nhiều mã tham chiếu cùng một HelperCode. Một tùy chọn khác là đặt HelperCodeIdcột là Unique.
VansFannel

@ypercube Bạn có thể vui lòng thêm câu sql hoàn chỉnh làm câu trả lời không? Tôi không làm việc rất thường xuyên với sql và tôi không biết làm thế nào để làm điều đó. Cảm ơn.
VansFannel

Về mặt khái niệm, các kỹ sư DBMS không thể cho phép các NULL trong các khóa chính mà không đi ngược lại toàn bộ mô hình dữ liệu quan hệ. Và mô hình quan hệ là một phần của những gì làm cho cơ sở dữ liệu quan hệ trở nên hữu ích. Bạn có thể hoặc không quan tâm đến khía cạnh này, nhưng điều quan trọng là chỉ ra cho khách truy cập trong tương lai.
Walter Mitty

@WalterMitty Tôi không bao giờ hiểu tại sao có giá trị null trong PK sẽ phá hủy giá trị mà RDBMS mang lại. Tôi đã nghe nó nhiều lần. Bạn có thể xây dựng?
usr

Câu trả lời:


24

Để trả lời câu hỏi trong tiêu đề, không, tất cả các cột chính phải có NOT NULL.

Nhưng không thay đổi thiết kế của các bảng, bạn có thể thêm một chỉ mục được lọc trên Code (HelperCodeId)cột:

CREATE UNIQUE INDEX 
    FUX_Code_HelperCodeId
ON dbo.Code 
    (HelperCodeId) 
WHERE 
    HelperCodeId IS NOT NULL ;

Bộ lọc ( WHERE HelperCodeId IS NOT NULL) là cần thiết vì cách SQL-Server xử lý null trong các ràng buộc duy nhất và các chỉ mục duy nhất. Nếu không có bộ lọc, SQL-Server sẽ không cho phép nhiều hơn một dòng với NULLtrong HelperCodeId.


Một thiết kế thay thế sẽ được loại bỏ HelperCodeIdkhỏi Codevà thêm một bảng thứ ba sẽ lưu trữ các mối quan hệ Code- HelperCode. Mối quan hệ giữa hai thực thể dường như là Zero-or-One - to - Zero-or-One (cả hai Code đều không có HelperCode và HelperCode có thể được sử dụng bởi không có Code):

CREATE TABLE [dbo].[Code]
(
    [Id] NVARCHAR(20) NOT NULL, 
    [Level] TINYINT NOT NULL, 
    [CommissioningFlag] TINYINT NOT NULL, 
    [SentToRanger] BIT NOT NULL DEFAULT 0, 
    [LastChange] NVARCHAR(50) NOT NULL, 
    [UserName] NVARCHAR(50) NOT NULL, 
    [Source] NVARCHAR(50) NOT NULL, 
    [Reason] NVARCHAR(200) NULL, 
    -- 
    -- removed:   [HelperCodeId] NVARCHAR(20) NULL,
    -- 
    CONSTRAINT [PK_Code] PRIMARY KEY CLUSTERED
    (
        [Id] ASC
    ),
    CONSTRAINT [FK_Code_LevelConfiguration]
       FOREIGN KEY ([Level])
        REFERENCES [dbo].[LevelConfiguration] ([Level]),
) ;

HelperCode vẫn không thay đổi:

CREATE TABLE [dbo].[HelperCode]
(
    [HelperCodeId] NVARCHAR(20) NOT NULL, 
    [Level] TINYINT NOT NULL, 
    [CommissioningFlag] TINYINT NOT NULL, 
    [LastChange] NVARCHAR(50) NOT NULL,
    CONSTRAINT [PK_HelperCode] PRIMARY KEY CLUSTERED
    (
        [HelperCodeId] ASC
    ),
    CONSTRAINT [FK_HelperCode_LevelConfiguration]
       FOREIGN KEY ([Level])
        REFERENCES [dbo].[LevelConfiguration] ([Level])
) ;

Bảng bổ sung sẽ có hai liên kết UNIQUE(hoặc một chính và một duy nhất) để đảm bảo rằng mỗi Mã có liên quan đến (tối đa) một HelperCode và mỗi HelperCode có liên quan đến (tối đa) một Mã. Cả hai cột sẽ là NOT NULL:

CREATE TABLE [dbo].[Code_HelperCode]
(
    [CodeId] NVARCHAR(20) NOT NULL, 
    [HelperCodeId] NVARCHAR(20) NOT NULL, 
    CONSTRAINT [UQ_Code_HelperCode_CodeId]
       UNIQUE (CodeId),
    CONSTRAINT [UQ_Code_HelperCode_HelperCodeId]
       UNIQUE (HelperCodeId),
    CONSTRAINT [FK_HelperCode_Code]
       FOREIGN KEY ([CodeId])
        REFERENCES [dbo].[Code] ([Id]),
    CONSTRAINT [FK_Code_HelperCode]
       FOREIGN KEY ([HelperCodeId])
        REFERENCES [dbo].[HelperCode] ([HelperCodeId])
) ;

Cảm ơn, bạn có thể thay đổi thiết kế nếu bạn muốn. Tôi có thể học được rất nhiều.
VansFannel

Cảm ơn thiết kế của bạn. Tôi chưa thêm một bảng mới vì tôi nghĩ những bảng này chỉ được sử dụng trong mối quan hệ nhiều-nhiều.
VansFannel

0

Thay vào đó, hãy thử sử dụng một ràng buộc duy nhất. Giả sử tiêu chuẩn ANSI tuyên bố null là khóa chính là không hợp lệ, nhưng tôi chưa bao giờ thấy tiêu chuẩn này và không muốn mua nó để xác minh điều này.

Không có khóa null dường như là một trong những điều mà các nhà phát triển có niềm tin rất khó khăn theo cách này hay cách khác. Sở thích của tôi là sử dụng chúng vì tôi thấy nó hữu ích cho các bảng tra cứu có chứa các chú giải công cụ và dữ liệu liên quan cho các hộp tổ hợp không được điền.

Tôi được dạy rằng giá trị Null chỉ ra rằng một biến chưa bao giờ được đặt và giá trị trống cho biết giá trị đã được đặt trong quá khứ. Tất nhiên điều này tùy thuộc vào nhà phát triển để xác định cho ứng dụng, nhưng tôi thấy nó vô lý khi cho phép các khóa chính trống nhưng không phải là khóa chính null.

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.