【问题标题】:Why to use "not null primary key" in TSQL?为什么在 SQL 中使用“非空主键”?
【发布时间】:2011-04-23 18:31:33
【问题描述】:

我在博客、文章、书籍、论坛、SO 等中的 TSQL 中创建表的脚本中经常看到“非空主键”。

例如,如果要搜索“not null” NEAR “primary key” “sql server”,BING 会给出 630,000 个结果((仅英文):
http://www.bing.com/search?q=%22not+null%22+NEAR+%22primary+key%22+%22sql+server%22&go=&form=QBRE&filt=lf

我一直认为“NOT NULL”是 PRIMARY KEY 定义不可分割的一部分,至少在 SQL Server 中是这样。
我尝试在 SQL Server 中创建带有和不带有“NOT NULL”的主键的表,但无法掌握可能的区别。

为什么即使在快速(短)插图中也都使用“NOT NULL”来创建主键?


更新:
NULL 如何标识任何内容或唯一性(以及如果允许一个 NULL,也可以防止多个 NULL)?它是未指定的、缺失的、不适用的值

我的问题还暗示了子问题:
如果为 PK 定义了“NOT NULL”,为什么没有指定 UNIQUE?
PK的定义是什么。是 UNIQUE CONSTRAINT + NOT NULL 还是 UNIQUE INDEX(那么为什么不是 NULL)?
请给我关于它的 msdn 文档的链接。

更新 2: @Damien_The_Unbeliever

为什么没有“NOT NULL”就不是同义词?

CREATE TABLE T3 
(
    PK int -- NOT NULL commented out
    , nonPK int -- to double-check my sanity 
    , constraint PK_vgv8 PRIMARY KEY (PK) on [PRIMARY]
) 

仍然不允许 NULL 给出:


  • Microsoft SQL Server 管理工作室


    没有更新任何行。

    第 2 行中的数据未提交。 错误来源:.Net SqlClient 数据提供程序。 错误消息:无法将值 NULL 插入列 'PK',表 'TestData.dbo.T3';列不允许空值。插入失败。

    语句已终止。

    更正错误并重试或按 ESC 取消更改。


    确定帮助


更新3:
这个,概念和术语定义,可能看起来不切实际的麻烦。
并非如此,在基本概念上的一些错误陈述(在沟通/讨论期间其他人的意见)足以被视为白痴,并在专业互动和沟通中造成障碍。

我忘了说,NOT NULL 是 SSMS 为 PK 编写的脚本!

【问题讨论】:

  • 如果记录可以被唯一标识,你将如何找到它?????
  • @InSane,我已经习惯了 GOOGLE,但 BING 碰巧找到了 GOOGLE 没有的结果(尤其是已删除网页的缓存)。此外,当我只用英语搜索时,google.com 重定向到 google.ru 给了我很多西里尔文(俄语)的结果。
  • @leppie,我不明白,SQL Server 中的“PK = UNIQUE + Not NuLL”(?),请发布您的答案
  • @vgv8:您可以将其设为 NULL,但由于 PK 唯一性约束,这将只允许 1 个和 1 个单个 NULL 条目。我认为您可以同意我的观点,即单个 NULL PK 是荒谬的。
  • @leppie,我如何在 PK 中有 NULL?发表你的答案

标签: sql-server tsql database-design primary-key


【解决方案1】:

为清晰起见进行了编辑

根据 SQL 规范,主键不能包含 NULL。这意味着用“NOT NULL PRIMARY KEY”或“PRIMARY KEY”装饰列应该做同样的事情。但是您正确地遵循 SQL 标准依赖于有问题的 SQL 引擎。例如,由于早期的错误,SQL Lite 没有正确实现标准并允许在非整数主键中使用空值(请参阅sqlite.org/lang_createtable.html)。这将意味着(至少)SQLLite 这两条语句做了两件不同的事情。

由于“NOT NULL PRIMARY KEY”清楚地表明意图并继续强制非空值,即使主键被删除,这应该是首选语法。

【讨论】:

  • 我询问了没有“NOT NULL”的 PK,而不是没有 PK 的“NOT NULL”。我无法以任何可能的方式在 PK 字段中设置 NULL。
  • @vgv8:很抱歉造成混淆,我是说你可以只使用“主键”或“非空主键”,它们都会做同样的事情。但是如果您删除了“PRIMARY KEY”,则第一个语句将允许该列允许空值,而另一个则不会,换句话说,只有第二个真正描述了所需的意图,而第一个则利用了 snytax 短切。我敢肯定他们不会,但如果 MS 将 SQL 更改为允许主键中有一个空值,那么这两种行为将完全不同。
  • 我建议省略手续。像@Paul Hadfield,如果没有其他人参与讨论。对于评论中给出的链接,我更赞成您的答案,这有助于洞察力和揭示一些定义或方法,而不是主要答案本身。我还发布了子问题stackoverflow.com/questions/3906811/…。有一些基本概念和原则,我认为它们应该是通用的,以及针对不同 RDBMS 的特定实现
  • 我在我提到的子问题中对您在 cmets 中的回复表示感谢。您不能将链接添加到答案中吗?它使我免于在那里大放异彩
【解决方案2】:

这在语法上不是必需的,因为如果没有为主键列指定,不管其他列的默认设置如何,这将是默认值。

SET ANSI_NULL_DFLT_ON ON /*Set default for columns to allow NULL if not specified*/

CREATE TABLE #t
(
i INT PRIMARY KEY,
j INT
)

SELECT name,is_nullable
FROM tempdb.sys.columns 
WHERE object_id=object_id('tempdb..#t')


INSERT INTO #t VALUES (1,NULL) /*Succeeds as j allows NULL*/

INSERT INTO #t VALUES (NULL, 1)/*Won't succeed as column doesn't allow nulls*/

DROP TABLE #t

【讨论】:

  • +1 支持我的问题。在发布之前,我确实运行过这样的脚本。我仍然希望有明确的定义和原因的答案。这是基本概念,在向某人回答如此简单的基本问题时,我不想让自己成为小丑
【解决方案3】:

所有主键意味着它是表的标识符,因此根据定义是唯一的。唯一并不意味着不为空,因此如果您要求主键不能为空,则必须如此明确地指定。如果你允许 null,你只是说在列中可以有一个 null 值,但你只能有一个,因为它必须是唯一的。

【讨论】:

  • 您不必明确指定它。将其设为 PK 就暗示了这一点。
【解决方案4】:

以下内容:

CREATE TABLE T1 (
    T1ID int not null primary key
)

实际上是以下内容的简写:

CREATE TABLE T1 (
    T1ID int not null,
    constraint <system generated name> PRIMARY KEY (T1ID) on [PRIMARY]
)

列定义与约束定义完全不同。只是速记隐藏了它是两个独立定义的事实。 (当然,如果您使用长手形式,您可以执行跨列定义主键等操作。

编辑,以响应更新。不,您仍然不能在主键中有可为空的列。但考虑到在 create table 语句中指定每列的可空性是正常的,省略它只是感觉很奇怪(即使它是 PRIMARY KEY 约束隐含/要求的)。

CREATE TABLE T1 (
    T1ID int,
    constraint <system generated name> PRIMARY KEY (T1ID) on [PRIMARY]
)

创建与之前的代码示例完全相同的表。因此,明确指定列的“非空”能力更多是对结果的明确确认,而不是要求。

最后,您会发现它经常出现在示例代码中,因为人们已经开发了他们的数据库,然后使用内置的 SQL 工具从他们的数据库生成脚本 - 脚本工具总是明确地放置所有内容。

【讨论】:

  • 我没有问我能不能。我问为什么除了 SSMS 定义的“主键”TSQL 之外,还要坚持编写“NOT NULL”脚本
【解决方案5】:

我不知道任何允许在 PRIMARY KEY 约束中使用可为空列的 DBMS。 SQL 标准规定 PRIMARY KEY 约束中的列不能为空。

在关系模型中,候选键由唯一值组成,而不是空值。 SQL 的一个特点是可以选择在 UNIQUE 约束中使用可空列。该功能可能是造成许多数据质量问题的原因。我强烈建议您避免在 UNIQUE 约束中使用可为空的列。如有必要,为不可为空的值创建一个新表并对其施加 UNIQUE 约束。

【讨论】:

    【解决方案6】:

    MYSQL 的有趣之处在于,当您将 NULL(或 0)值传递给 primary key auto_increment 字段时,这种行为非常好。

    说你有

    create table Users(
    
      userId int primary key auto_increment
    
    ) ;
    
    insert into users( userId ) values ( null ) ; -- insert OK
    select * from Users ;  -- Shows 1
    

    因此,如果您将 0 或 NULL 传递给 auto_increment 字段,它只会选择下一个可用值。

    如果您关闭auto_increment 属性,则使用NULL 插入PRIMARY KEY 字段会失败,因为PRIMARY KEY 不能为空,这是错误。所以PRIMARY KEY NOT NULL 上的NOT NULL 至少在 MySQL 中是隐含的。

    【讨论】:

      【解决方案7】:

      我似乎要朝另一个方向解释为什么当主键本身隐含地保持不为空时,为什么不为空并且主键也被字段接受。

      假设我们有表 table1,这意味着它将保存其值来存储二维数据。如果我创建一个只有一个字段的表是可以接受的,但它在现实世界中的实时用法是什么?我相信它将是一维表,只存储一个字段的数据导致没有信息。并且保持该字段接受 Null 值是没有意义的。

      现在,让我们假设,我们定义了一个包含 2 个字段的表,这意味着当填充了适当的数据时,我们可以从每一行中提取好的信息。

      如果我们想要推送垃圾或者期望 count(*) 应该导致额外的行,那么让这 2 个字段接受 Null 值是没有任何意义的。

      所以,我的说法是,如果我们希望使用具有良好基本 RDBMS 概念的规范化表,至少对于 2 个字段来说,不为 null 是强制性的。在考虑表是否需要主键作为约束之前,我们总是将字段定义为非空。此外,如果需要,请使用主键并注意您可以随时删除它,但不能“不为空”

      如果我们确定我们不会删除主键,则只使用主键,否则既不为空也不使用主键。

      这完全取决于使用情况,正如我所说,必须达到基本的表定义和 Codd 规则才能获得良好的 DB。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-16
        • 2021-03-19
        • 1970-01-01
        • 2023-02-04
        • 2021-10-10
        • 2013-12-22
        • 1970-01-01
        • 2021-02-21
        相关资源
        最近更新 更多