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

Van*_*nel 22 database-design sql-server-2012

我正在开发一个 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])
)
Run Code Online (Sandbox Code Playgroud)

那是对的吗?

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

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

ype*_*eᵀᴹ 28

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

但是不改变表的设计,你可以在Code (HelperCodeId)列上添加一个过滤索引:

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

WHERE HelperCodeId IS NOT NULL由于 SQL-Server 在唯一约束和唯一索引中处理空值的方式,需要过滤器 ( )。如果没有过滤器,SQL-Server 将不允许超过一行的NULLin HelperCodeId


另一种设计是删除HelperCodeIdfromCode并添加第三个表来存储Code-HelperCode关系。两个实体之间的关系似乎是零或一到零或一(一个 Code 可以没有 HelperCode,一个 HelperCode 可能不被代码使用):

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]),
) ;
Run Code Online (Sandbox Code Playgroud)

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])
) ;
Run Code Online (Sandbox Code Playgroud)

附加表将有两个UNIQUE约束(或一个主要和一个唯一)以确保每个 Code 与(最多)一个 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])
) ;
Run Code Online (Sandbox Code Playgroud)