Könnte eine Nullspalte Teil eines Primärschlüssels sein?

15

Ich entwickle eine SQL Server 2012-Datenbank und habe eine Frage zu einer Eins-zu-Null-Oder-Eins-Beziehung.

Ich habe zwei Tische Codesund HelperCodes. Ein Code kann null oder einen Hilfscode haben. Dies ist das SQL-Skript zum Erstellen dieser beiden Tabellen und ihrer Beziehungen:

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

Ist das korrekt?

Ein Code und ein HelperCode sind unterschiedliche Entitäten. Ein HelperCode kann ein verwendeter (kein Code verweist darauf) oder ein verwendeter (nur ein Code verweist darauf) sein.

Möglicherweise muss Code.HelperCodeId Teil des Primärschlüssels der Codetabelle sein. Ich bin mir aber nicht sicher, ob eine Nullspalte Teil einer Primärspalte sein könnte. Auf diese Weise möchte ich verhindern, dass zwei oder mehr Codes auf denselben HelperCode verweisen.

VansFannel
quelle
1
Warum würdest du HelperCodeIdein Teil der PK sein wollen? Liegt es vielleicht daran, dass Sie verhindern möchten, dass zwei oder mehr Codes auf denselben HelperCode verweisen?
Andriy M
Ja, ich möchte verhindern, dass zwei oder mehr Codes auf denselben HelperCode verweisen. Eine andere Option besteht darin, die HelperCodeIdSpalte als eindeutig festzulegen.
VansFannel,
@ypercube Könnten Sie bitte den vollständigen SQL-Satz als Antwort hinzufügen? Ich arbeite nicht sehr oft mit SQL und weiß nicht, wie ich es machen soll. Vielen Dank.
VansFannel,
Konzeptionell hätten die DBMS-Ingenieure keine NULL-Werte in Primärschlüsseln zulassen können, ohne das gesamte relationale Datenmodell zu verletzen. Und das relationale Modell ist Teil dessen, was relationale Datenbanken so nützlich macht. Möglicherweise interessiert Sie dieser Aspekt nicht, aber es ist wichtig, auf zukünftige Besucher hinzuweisen.
Walter Mitty
@ WalterMitty Ich habe nie verstanden, warum ein Nullwert in einem PK den Wert, den ein RDBMS bringt, zerstören würde. Ich habe es oft gehört. Können Sie näher darauf eingehen?
USR

Antworten:

24

Um die Frage im Titel zu beantworten, müssen alle Primärspalten nein sein NOT NULL.

Ohne das Design der Tabellen zu ändern, können Sie der Code (HelperCodeId)Spalte einen gefilterten Index hinzufügen :

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

Der filter ( WHERE HelperCodeId IS NOT NULL) wird benötigt, weil SQL-Server Nullen in eindeutigen Einschränkungen und eindeutigen Indizes behandelt. Ohne den Filter würde SQL-Server nicht mehr als eine Zeile mit NULLin zulassen HelperCodeId.


Ein alternatives Design wäre das Entfernen HelperCodeIdvon Codeund Hinzufügen einer dritten Tabelle, in der die Code- HelperCodeBeziehungen gespeichert werden . Die Beziehung zwischen den beiden Entitäten scheint Null oder Eins zu Null oder Eins zu sein (beide, ein Code kann keinen HelperCode haben und ein HelperCode kann von keinem Code verwendet werden):

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 bleibt unverändert:

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

Die zusätzliche Tabelle enthält zwei UNIQUEBedingungen (oder eine primäre und eine eindeutige), um sicherzustellen, dass jeder Code (maximal) einem HelperCode und jeder HelperCode (maximal) einem Code zugeordnet ist. Beide Spalten wären 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ᵀᴹ
quelle
Vielen Dank, Sie können das Design ändern, wenn Sie möchten. Ich könnte viel lernen.
VansFannel,
Vielen Dank für Ihr Design. Ich habe keine neue Tabelle hinzugefügt, da ich dachte, dass diese Tabellen nur in einer Viele-zu-Viele-Beziehung verwendet werden.
VansFannel
0

Verwenden Sie stattdessen eine eindeutige Einschränkung. Angeblich hat der ANSI-Standard Nullen als ungültigen Primärschlüssel deklariert, aber ich habe den Standard noch nie gesehen und möchte ihn nicht kaufen, um dies zu überprüfen.

Das Fehlen von Null-Schlüsseln scheint eines der Dinge zu sein, an die Entwickler auf die eine oder andere Weise nur sehr schwer glauben. Ich bevorzuge es, sie zu verwenden, da ich sie für Nachschlagetabellen mit QuickInfos und zugehörigen Daten für Kombinationsfelder hilfreich finde, die nicht ausgefüllt wurden.

Mir wurde beigebracht, dass ein Nullwert angibt, dass noch nie eine Variable festgelegt wurde, und ein leerer Wert angibt, dass der Wert in der Vergangenheit festgelegt wurde. Natürlich muss der Entwickler dies für die Anwendung definieren, aber ich finde es unsinnig, leere Primärschlüssel, aber keine Null-Primärschlüssel zuzulassen.

Kevin
quelle