Una colonna null potrebbe far parte di una chiave primaria?


15

Sto sviluppando un database SQL Server 2012 e ho una domanda su una relazione One-to-Zero-Or-One.

Ho due tavoli Codese HelperCodes. Un codice può avere zero o un codice helper. Questo è lo script sql per creare queste due tabelle e le loro relazioni:

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

È corretto?

Un codice e un HelperCode sono entrambe entità diverse. Un HelperCode può essere un usato (nessuno Codice lo fa riferimento), o usato (solo un Codice lo fa riferimento).

Forse Code.HelperCodeId deve far parte della chiave primaria della tabella dei codici. Ma non sono sicuro che una colonna nulla possa far parte di una primaria. In questo modo, desidero impedire che due o più codici facciano riferimento allo stesso HelperCode.


1
Perché vorresti HelperCodeIdfar parte del PK? È, per caso, perché vuoi impedire a due o più codici di fare riferimento allo stesso HelperCode?
Andriy M,

Sì, desidero impedire che due o più codici facciano riferimento allo stesso HelperCode. Un'altra opzione è impostare la HelperCodeIdcolonna come Unica.
VansFannel,

@ypercube Potresti aggiungere la frase sql completa come risposta? Non lavoro molto spesso con sql e non so come farlo. Grazie.
VansFannel,

Concettualmente, gli ingegneri DBMS non avrebbero potuto consentire NULL nelle chiavi primarie senza andare contro l'intero modello di dati relazionali. E il modello relazionale fa parte di ciò che rende i database relazionali così utili. Potresti essere interessato o meno a questo aspetto, ma è importante sottolineare per i futuri visitatori.
Walter Mitty

@WalterMitty Non ho mai capito perché avere un valore null in un PK distruggerebbe il valore che porta un RDBMS. L'ho sentito molte volte. Puoi elaborare?
usr

Risposte:


24

Per rispondere alla domanda nel titolo, no, tutte le colonne primarie devono essere NOT NULL.

Ma senza alterare il design delle tabelle, è possibile aggiungere un indice filtrato sulla Code (HelperCodeId)colonna:

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

Il filtro ( WHERE HelperCodeId IS NOT NULL) è necessario a causa del modo in cui SQL Server tratta i valori null in vincoli univoci e indici univoci. Senza il filtro, SQL Server non consentirebbe più di una riga con NULLin HelperCodeId.


Un design alternativo sarebbe rimuovere il HelperCodeId da Codee aggiungere una terza tabella che memorizzerà i Code- HelperCoderelazioni. La relazione tra le due entità sembra essere Zero-or-One - to - Zero-or-One (entrambi i codici non possono avere HelperCode e HelperCode non può essere utilizzato da nessun codice):

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 Rimane invariato:

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

La tabella aggiuntiva avrà due UNIQUEcontrapposizioni (o una primaria e una univoca) per garantire che ogni Codice sia correlato a (massimo) un Codice Helper e ogni Codice Helper sia correlato a (Massimo) un Codice. Entrambe le colonne sarebbero 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])
) ;

Grazie, potresti modificare il design se lo desideri. Potrei imparare molto.
VansFannel,

Grazie per il tuo design. Non ho aggiunto una nuova tabella perché pensavo che queste tabelle fossero utilizzate solo in una relazione molti-a-molti.
VansFannel,

0

Prova invece a utilizzare un vincolo univoco. Presumibilmente lo standard ANSI ha dichiarato nulli come chiave primaria non validi, ma non ho mai visto lo standard e non desidero acquistarlo per verificarlo.

Non avere chiavi null sembra essere una di quelle cose che gli sviluppatori credono molto duramente in un modo o nell'altro. La mia preferenza è di usarli perché lo trovo utile per le tabelle di ricerca contenenti descrizioni comandi e dati correlati per caselle combinate che non sono state popolate.

Mi è stato insegnato che il valore Null indica che non è mai stata impostata una variabile e che il valore vuoto indica che il valore è stato impostato in passato. Ovviamente spetta allo sviluppatore definire l'applicazione, ma trovo privo di senso consentire chiavi primarie vuote ma non chiavi primarie nulle.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.