空列可以成为主键的一部分吗?


15

我正在开发一个SQL Server 2012数据库,并且对一个一对零或一个关系存在疑问。

我有两个表,CodesHelperCodes。一个代码可以具有零个或一个辅助代码。这是用于创建这两个表及其关系的sql脚本:

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

那是对的吗?

Code和HelperCode都是不同的实体。一个HelperCode可以是二手的(没有代码引用它),也可以是二手的(只有一个Code引用它)。

也许Code.HelperCodeId必须是代码表主键的一部分。但是我不确定空列是否可以成为主数据库的一部分。这样做,我想防止两个或多个代码引用相同的HelperCode。


1
您为什么要HelperCodeId成为PK的一部分?是否是因为您想防止两个或多个代码引用同一HelperCode而引起的吗?
Andriy M

是的,我想防止两个或多个代码引用相同的HelperCode。另一种选择是将HelperCodeId列设置为“唯一”。
VansFannel

@ypercube您能否将完整的sql语句添加为答案?我不经常使用sql,也不知道该怎么做。谢谢。
VansFannel

从概念上讲,在不违背整个关系数据模型的情况下,DBMS工程师不能在主键中允许NULL。关系模型是使关系数据库如此有用的部分。您可能对此感兴趣,也可能不感兴趣,但是对未来的访问者指出这一点很重要。
Walter Mitty

@WalterMitty我从不理解为什么在PK中使用空值会破坏RDBMS带来的值。我已经听过很多次了。你能详细说明吗?
usr

Answers:


24

要回答标题中的问题,否,所有主要列都必须为NOT NULL

但是可以在不更改表设计的情况下,在Code (HelperCodeId)列上添加过滤索引:

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

WHERE HelperCodeId IS NOT NULL之所以需要过滤器(),是因为SQL-Server处理唯一约束和唯一索引中的null的方式。没有过滤器,SQL Server将不允许NULLin中包含多于一行HelperCodeId


一种替代设计是从中删除HelperCodeIdfrom Code并添加第三个表,该表将存储Code- HelperCode关系。这两个实体之间的关系似乎是零或一对零(一个代码都不能有HelperCode,并且一个HelperCode可能不被任何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 保持不变:

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

附加表将具有两个UNIQUE约束(或一个主要约束,一个唯一约束),以确保每个代码都与(最多)一个HelperCode相关,并且每个HelperCode都与(最大)一个Code相关。两列都是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])
) ;

谢谢,您可以根据需要更改设计。我可以学到很多东西。
VansFannel

感谢您的设计。我没有添加新表,因为我认为这些表仅用于多对多关系。
VansFannel

0

请尝试使用唯一约束。假设ANSI标准将null声明为无效的主键,但是我从未见过该标准,也不希望购买该标准来进行验证。

没有空键似乎是开发人员很难以一种或另一种方式相信的事情之一。我首选使用它们,因为我发现它对包含工具提示的查询表和未填充的组合框的相关数据很有帮助。

有人告诉我,空值表示从未设置过变量,空值表示过去已设置过该值。当然,这要由开发人员为应用程序定义,但是我发现允许空的主键而不是空的主键是没有意义的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.