Uma coluna nula poderia fazer parte de uma chave primária?

15

Estou desenvolvendo um banco de dados do SQL Server 2012 e tenho uma pergunta sobre um relacionamento um para zero ou um.

Eu tenho duas mesas Codese HelperCodes. Um código pode ter zero ou um código auxiliar. Este é o script sql para criar essas duas tabelas e seus relacionamentos:

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

Isso está correto?

Um código e um HelperCode são entidades diferentes. Um HelperCode pode ser usado (nenhum código faz referência a ele) ou usado (apenas um código faz referência a ele).

Talvez Code.HelperCodeId deva fazer parte da chave primária da tabela Código. Mas não tenho certeza se uma coluna nula pode fazer parte de uma primária. Fazendo isso, quero impedir que dois ou mais códigos façam referência ao mesmo HelperCode.

VansFannel
fonte
1
Por que você gostaria HelperCodeIdde fazer parte do PK? É por acaso que você deseja impedir que dois ou mais códigos façam referência ao mesmo HelperCode?
Andriy H
Sim, quero impedir que dois ou mais códigos façam referência ao mesmo HelperCode. Outra opção é definir a HelperCodeIdcoluna como Exclusiva.
VansFannel
@ypercube Você poderia adicionar a sentença sql completa como resposta? Não trabalho muito com sql e não sei como fazê-lo. Obrigado.
VansFannel
Conceitualmente, os engenheiros do DBMS não poderiam ter permitido NULLs em chaves primárias sem ir contra todo o modelo de dados relacionais. E o modelo relacional faz parte do que torna os bancos de dados relacionais tão úteis. Você pode ou não estar interessado nesse aspecto, mas é importante destacar para futuros visitantes.
22815 Walter Mitty
@WalterMitty Eu nunca entendi por que ter um valor nulo em uma PK destruiria o valor que um RDBMS traz. Eu já ouvi isso muitas vezes. Você pode elaborar?
usr

Respostas:

24

Para responder à pergunta no título, não, todas as colunas principais precisam estar NOT NULL.

Mas sem alterar o design das tabelas, você pode adicionar um índice filtrado na Code (HelperCodeId)coluna:

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

O filter ( WHERE HelperCodeId IS NOT NULL) é necessário devido à maneira como o SQL-Server trata nulos em restrições e índices exclusivos. Sem o filtro, o SQL-Server não permitiria mais de uma linha com NULLin HelperCodeId.


Um projeto alternativo seria remover a HelperCodeIdpartir de Codee adicionar uma terceira tabela que armazenará os Code- HelperCoderelacionamentos. O relacionamento entre as duas entidades parece ser Zero-ou-Um-para-Zero-ou-Um (tanto um Código não pode ter HelperCode quanto um HelperCode pode ser usado por nenhum Código):

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 continua sem alteração:

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

A tabela adicional terá duas UNIQUErestrições (ou uma primária e uma única) para garantir que todo Código esteja relacionado a (no máximo) um HelperCode e cada HelperCode esteja relacionado a (no máximo) um Código. Ambas as colunas seriam 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ᵀᴹ
fonte
Obrigado, você pode alterar o design, se quiser. Eu pude aprender muito.
VansFannel
Obrigado pelo seu design. Não adicionei uma nova tabela porque pensei que essas tabelas fossem usadas apenas no relacionamento muitos-para-muitos.
VansFannel 19/09/2015
0

Tente usar uma restrição exclusiva. Supostamente, o padrão ANSI declarou nulos como chave primária como inválido, mas nunca vi o padrão e não desejo comprá-lo para verificar isso.

Não ter chaves nulas parece ser uma daquelas coisas que os desenvolvedores acreditam muito de uma maneira ou de outra. Minha preferência é usá-los porque acho útil para tabelas de pesquisa que contêm dicas de ferramentas e dados relacionados para caixas de combinação que não foram preenchidas.

Fui ensinado que o valor nulo indica que uma variável nunca foi definida e o valor vazio indica que o valor foi definido no passado. É claro que depende do desenvolvedor definir para o aplicativo, mas acho absurdo permitir chaves primárias vazias, mas não chaves primárias nulas.

Kevin
fonte