Estou desenvolvendo um banco de dados SQL Server 2012 e tenho uma pergunta sobre um relacionamento um para zero ou um.
Eu tenho duas tabelas, Codes
e 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 é correto?
Um Code 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 deve fazer parte da chave primária da tabela de códigos. Mas não tenho certeza se uma coluna nula pode fazer parte de uma primária. Fazendo isso, quero evitar que dois ou mais Codes façam referência ao mesmo HelperCode.
Para responder à pergunta do título, não, todas as colunas primárias precisam ser
NOT NULL
.Mas sem alterar o design das tabelas, você pode adicionar um índice filtrado na
Code (HelperCodeId)
coluna:O filtro (
WHERE HelperCodeId IS NOT NULL
) é necessário devido à maneira como o SQL-Server trata nulos em restrições exclusivas e índices exclusivos. Sem o filtro, o SQL-Server não permitiria mais de uma linhaNULL
comHelperCodeId
.Um design alternativo seria remover o
HelperCodeId
fromCode
e adicionar uma terceira tabela que armazenará os relacionamentosCode
-HelperCode
. A relação entre as duas entidades parece ser Zero-ou-One-to-Zero-or-One (tanto um Code pode não ter HelperCode quanto um HelperCode pode ser usado por nenhum Code):HelperCode
permanece inalterado:A tabela adicional terá duas
UNIQUE
restrições (ou uma primária e uma exclusiva) para garantir que cada 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 seriamNOT NULL
:Tente usar uma restrição exclusiva. Supostamente, o padrão ANSI declarou nulos como chave primária inválida, 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 têm uma crença muito difícil de uma forma ou de outra. Minha preferência é usá-los porque acho útil para tabelas de pesquisa contendo dicas de ferramentas e dados relacionados para caixas de combinação que não foram preenchidas.
Fui ensinado que o valor Null indica que uma variável nunca foi definida e o valor vazio indica que o valor foi definido no passado. É claro que isso cabe ao desenvolvedor definir para o aplicativo, mas acho absurdo permitir chaves primárias vazias, mas não chaves primárias nulas.