Bisakah kolom nol menjadi bagian dari kunci utama?

15

Saya sedang mengembangkan database SQL Server 2012 dan saya punya pertanyaan tentang hubungan One-to-Zero-Or-One.

Saya punya dua meja, Codesdan HelperCodes. Kode dapat memiliki nol atau satu kode pembantu. Ini adalah skrip sql untuk membuat dua tabel ini dan hubungannya:

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])
)

Apakah itu benar?

Kode dan HelperCode keduanya merupakan entitas yang berbeda. HelperCode dapat digunakan (tidak ada Kode yang merujuknya), atau digunakan (hanya satu Kode yang merujuknya).

Mungkin Code.HelperCodeId harus menjadi bagian dari kunci utama tabel kode. Tapi saya tidak yakin apakah kolom nol bisa menjadi bagian dari primer. Melakukan ini, saya ingin mencegah dua atau lebih kode referensi HelperCode yang sama.

VansFannel
sumber
1
Mengapa Anda ingin HelperCodeIdmenjadi bagian dari PK? Apakah itu, kebetulan, karena Anda ingin mencegah dua atau lebih Kode untuk referensi HelperCode yang sama?
Andriy M
Ya, saya ingin mencegah dua atau lebih kode referensi HelperCode yang sama. Pilihan lain adalah mengatur HelperCodeIdkolom sebagai Unik.
VansFannel
@ ypercube Bisakah Anda menambahkan kalimat sql yang lengkap sebagai jawaban? Saya tidak sering bekerja dengan sql dan saya tidak tahu bagaimana melakukannya. Terima kasih.
VansFannel
Secara konseptual, para insinyur DBMS tidak bisa mengizinkan NULL di kunci primer tanpa bertentangan dengan seluruh model data relasional. Dan model relasional adalah bagian dari apa yang membuat basis data relasional sangat berguna. Anda mungkin atau mungkin tidak tertarik pada aspek ini, tetapi penting untuk menunjukkan bagi pengunjung masa depan.
Walter Mitty
@WalterMitty Saya tidak pernah mengerti mengapa memiliki nilai nol dalam PK akan merusak nilai yang dibawa RDBMS. Saya sudah sering mendengarnya. Bisakah Anda menguraikan?
usr

Jawaban:

24

Untuk menjawab pertanyaan dalam judul, tidak, semua kolom utama harus NOT NULL.

Tetapi tanpa mengubah desain tabel, Anda bisa menambahkan indeks yang difilter pada Code (HelperCodeId)kolom:

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

Filter ( WHERE HelperCodeId IS NOT NULL) diperlukan karena cara SQL-Server memperlakukan nulls dalam batasan unik dan indeks unik. Tanpa filter, SQL-Server tidak akan memungkinkan lebih dari satu baris dengan NULLdi HelperCodeId.


Desain alternatif akan menghapus HelperCodeIddari Codedan menambahkan tabel ketiga yang akan menyimpan Code- HelperCodehubungan. Hubungan antara dua entitas tampaknya Zero-atau-One - to-Zero-atau-One (kedua Kode tidak dapat memiliki HelperCode dan HelperCode dapat digunakan oleh Kode tidak):

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 tetap tidak berubah:

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])
) ;

Tabel tambahan akan memiliki dua UNIQUEbatasan (atau satu primer dan satu unik) untuk memastikan bahwa setiap Kode terkait dengan (maksimum) satu HelperCode dan setiap HelperCode terkait dengan (maksimum) satu Kode. Kedua kolom tersebut adalah 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])
) ;
ypercubeᵀᴹ
sumber
Terima kasih, Anda dapat mengubah desainnya jika Anda mau. Saya bisa belajar banyak.
VansFannel
Terima kasih untuk desain kamu. Saya belum menambahkan tabel baru karena saya pikir tabel ini hanya digunakan dalam hubungan banyak ke banyak.
VansFannel
0

Coba gunakan batasan unik sebagai gantinya. Seharusnya standar ANSI menyatakan nulls sebagai kunci utama tidak valid, tetapi saya belum pernah melihat standar dan tidak ingin membelinya untuk memverifikasi ini.

Tidak memiliki kunci nol tampaknya menjadi salah satu hal yang para pengembang memiliki keyakinan sangat keras pada satu atau lain cara. Preferensi saya adalah menggunakannya karena menurut saya bermanfaat untuk tabel pencarian yang berisi tooltips dan data terkait untuk kotak kombo yang belum diisi.

Saya diajari bahwa nilai Null menunjukkan bahwa suatu variabel tidak pernah ditetapkan dan nilai kosong menunjukkan bahwa nilai tersebut telah ditetapkan sebelumnya. Tentu saja ini tergantung pada pengembang untuk menentukan aplikasi, tetapi saya merasa tidak masuk akal untuk mengizinkan kunci primer kosong tetapi bukan kunci primer nol.

kevin
sumber