Une colonne nulle pourrait-elle faire partie d'une clé primaire?

15

Je développe une base de données SQL Server 2012 et j'ai une question sur une relation un à zéro ou un.

J'ai deux tables, Codeset HelperCodes. Un code peut avoir zéro ou un code d'assistance. Voici le script sql pour créer ces deux tables et leurs relations:

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

Est-ce exact?

Un code et un HelperCode sont deux entités différentes. Un HelperCode peut être utilisé (aucun code ne le référence) ou utilisé (un seul code le référence).

Peut-être que Code.HelperCodeId doit faire partie de la clé primaire de la table Code. Mais je ne sais pas si une colonne nulle pourrait faire partie d'un primaire. Ce faisant, je veux empêcher que deux ou plusieurs codes référencent le même HelperCode.

VansFannel
la source
1
Pourquoi voudriez-vous HelperCodeIdfaire partie du PK? Est-ce, par hasard, parce que vous voulez empêcher deux codes ou plus de référencer le même HelperCode?
Andriy M
Oui, je veux empêcher que deux ou plusieurs codes fassent référence au même HelperCode. Une autre option consiste à définir la HelperCodeIdcolonne comme Unique.
VansFannel
@ypercube Pourriez-vous s'il vous plaît ajouter la phrase sql complète comme réponse? Je ne travaille pas très souvent avec sql et je ne sais pas comment le faire. Merci.
VansFannel
Sur le plan conceptuel, les ingénieurs du SGBD n'auraient pas pu autoriser les valeurs NULL dans les clés primaires sans aller à l'encontre de l'ensemble du modèle de données relationnelles. Et le modèle relationnel fait partie de ce qui rend les bases de données relationnelles si utiles. Cet aspect peut vous intéresser ou non, mais il est important de le signaler aux futurs visiteurs.
Walter Mitty
@WalterMitty Je n'ai jamais compris pourquoi avoir une valeur nulle dans un PK détruirait la valeur apportée par un SGBDR. Je l'ai entendu plusieurs fois. Peux-tu élaborer?
usr

Réponses:

24

Pour répondre à la question dans le titre, non, toutes les colonnes principales doivent l'être NOT NULL.

Mais sans modifier la conception des tables, vous pouvez ajouter un index filtré sur la Code (HelperCodeId)colonne:

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

Le filtre ( WHERE HelperCodeId IS NOT NULL) est nécessaire en raison de la façon dont SQL-Server traite les valeurs nulles dans les contraintes uniques et les index uniques. Sans le filtre, SQL-Server n'autoriserait pas plus d'une ligne avec NULLin HelperCodeId.


Une autre conception consisterait à supprimer le HelperCodeIdde Codeet à ajouter une troisième table qui stockera les relations Code- HelperCode. La relation entre les deux entités semble être Zero-or-One - to - Zero-or-One (un code ne peut pas avoir de HelperCode et un HelperCode peut être utilisé par aucun 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 reste inchangé:

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 table supplémentaire aura deux UNIQUEcontraintes (ou une principale et une unique) pour garantir que chaque code est lié à (maximum) un HelperCode et que chaque HelperCode est lié à (maximum) un code. Les deux colonnes seraient 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ᵀᴹ
la source
Merci, vous pouvez modifier le design si vous le souhaitez. Je pourrais apprendre beaucoup.
VansFannel
Merci pour votre conception. Je n'ai pas ajouté de nouvelle table car je pensais que ces tables ne sont utilisées que dans une relation plusieurs-à-plusieurs.
VansFannel
0

Essayez plutôt d'utiliser une contrainte unique. Soi-disant la norme ANSI a déclaré null comme une clé primaire non valide, mais je n'ai jamais vu la norme et je ne souhaite pas l'acheter pour le vérifier.

Ne pas avoir de clés nulles semble être l'une de ces choses que les développeurs ont une croyance très dure dans un sens ou dans l'autre. Ma préférence est de les utiliser, car je trouve cela utile pour les tables de recherche contenant des info-bulles et des données associées pour les zones de liste déroulante qui ne sont pas remplies.

On m'a appris que la valeur Null indique qu'une variable n'a jamais été définie et que la valeur vide indique que la valeur a été définie dans le passé. Bien sûr, c'est au développeur de définir pour l'application, mais je trouve absurde d'autoriser les clés primaires vides mais pas les clés primaires nulles.

Kevin
la source