我应该对NOT NULL列有多宽容?

时间:2009-03-17 23:33:44

标签: sql sql-server schema nullable

我正在设计一个数据库架构,我想知道我应该使用什么标准来决定每列是否应该nullable

我是否应该将那些绝对必须填写一行的列标记为NOT NULL,以便对我的应用程序有任何意义?

或者我应该标记我打算永远不会为空的所有列吗?

小型与大量NOT NULL列的性能影响是什么?

我假设很多NOT NULL列会减慢插入速度,但它实际上可能加快选择速度,因为查询执行计划生成器有更多关于列的信息..

比我知识更多的人可以给我低调吗?

15 个答案:

答案 0 :(得分:27)

老实说,我一直认为NOT NULL应该是默认值。 NULL是奇怪的特殊情况,每当你使用它时你应该为它做一个案例。另外,将列从NOT NULL更改为nullable要比从另一个方向更改容易得多。

答案 1 :(得分:14)

没有明显的性能后果。甚至不要考虑将此视为一个问题。这样做是一个巨大的早期优化反模式。

“我是否只应将那些绝对必须填写的列标记为NOT NULL才能对我的应用程序有任何意义?”

是。就这么简单。使用NULLable列而不使用任何NULL值比使用NULL并且不得不伪造它要好得多。无论如何,任何含糊不清的案例都会在您的业务规则中得到更好的过滤。

<小时/> 编辑:

我认为最可信的可空字段还有另一个论据,即Use Case参数。我们都受制于需要某些领域价值的数据输入表格;我们都放弃了我们对必填字段没有明智价值的形式。最终,应用程序,表单和数据库设计只有在反映用户需求时才是可辩护的;并且很明显,有许多数据库列,用户无法提供任何价值 - 有时在业务流程的某些特定点,有时甚至是。

答案 2 :(得分:11)

错误的一边是NOT NULL。在某些时候,您将不得不决定应用程序中的“含义” - 很可能,对于不同的列,它将是不同的东西。一些常见的情况是“未指定”,“未知”,“不适用”,“尚未发生”等等。您将知道何时需要其中一个值,然后您可以适当地允许NULLable列和编码它周围的逻辑。

允许随机事物为NULL,迟早总是是一场噩梦般的IME。小心谨慎地使用NULL - 并且知道它在逻辑中意味着什么。

编辑:似乎有一个想法,我正在争论 NO null列。这是荒谬的。 NULL 非常有用,但仅限于预期的位置。

Le Dorfier的DateOfDeath示例就是一个很好的例子。 NULL DateOfDeath将指示“尚未发生”。现在,我可以写一个视图LivingPersons WHERE DateOfDeath IS NULL

但是,NULL OrderDate是什么意思?订单还没有下订单?即使Order表中有记录? NULL地址怎么样?在让NULL成为值之前,这些是你应该想到的想法。

返回DateOfDeath - 人WHERE DateOfDeath > '1/1/1999'的查询不会返回NULL记录 - 即使我们逻辑上知道他们必须在1999年之后死亡。那是你要的吗?如果没有,那么最好在该查询中包含OR DateOfDeath IS NULL。如果允许所有列为NULL,则每次编写查询时都必须考虑 。 IME,对于10%左右的列,当它们为NULL时实际上具有合法含义时,这是太多的精神税。

答案 3 :(得分:10)

我发现将列标记为NOT NULL通常是一个好主意,除非你对列中的NULL有一个有用的含义。

,否则当你意识到你不想要它时,你可能会意外地在那里找到NULL,并且更改会更难。

答案 4 :(得分:9)

我尽量避免在数据库中使用NULL。这意味着字符字段始终不为空。对于数字字段也是如此,尤其是代表金钱或类似的东西(股票,单位等)。

我有两个例外:

  1. 可能不知道日期的日期(例如,DivorcedOn)
  2. 可选的foriegn密钥关系(MarriedToPersonId)。虽然有时我在外键表中使用了“空白”行并且必须强制关联(例如JobDescriptionCode)
  3. 我还偶尔使用显式位字段来表示“未知”/“未设置”(例如,JobDescriptionCode和IsEmployeed)。

    我有几个核心原因:

    1. NULL将始终在数字字段中导致问题。总是。总是。总是。无论你在某个点上多么小心选择X + Y,因为Total会发生并且它将返回NULL。
    2. NULL很容易在字符串字段中引起问题,通常是地址字段(例如,从地址中选择AddrLine1 + AddrLine2)。
    3. 防止业务逻辑层中的NULL是一种繁琐的浪费......只是不要让它们存在于数据库中,你可以节省100行代码。
    4. 我的首选默认值:

      • 字符串 - &gt; “”,又名空字符串
      • 数字 - &gt; 0
      • 日期 - &gt;今天或NULL(见例外#1)
      • 位 - &gt;假

答案 5 :(得分:7)

你可能会发现Chris Date的Database In Depth是这类问题的有用资源。你可以在这个interview中体验他的想法,他在其中说:

  

所以是的,我认为SQL非常糟糕。   但你明确地问它的主要内容   缺点是。嗯,这里有几个:

     
      
  • 重复行
  •   
  • 空值
  •   
  • 从左到右的列排序
  •   
  • 未命名的列和重复的列名称
  •   
  • 未能正确支持“=”
  •   
  • 指针
  •   
  • 高冗余
  •   

根据我自己的经验,使用具有基表外键的子表可以更好地表示几乎所有“计划空值”。参与子表是可选的,这是实际进行null / not null区分的地方。

这很好地解释了作为一阶逻辑命题的关系的解释。这也只是常识。当一个人不知道Bob的地址时,是否会写一个人的Rolodex:

Bob. ____

或者只是为了鲍勃填写地址卡,直到有人为他提供实际地址?

编辑:日期的参数显示在数据库深度的第53-55页,标题为“Why Nulls are Prohibited”下。

答案 6 :(得分:4)

我倾向于NOT NULL,除非我看到其他原因 - 就像别人说的那样,不管你喜不喜欢,NULL是奇怪的特殊情况。

关于NULL的我最喜欢的一个是:

SELECT F1 FROM T WHERE F2 <> 'OK'

...(至少在DB2中)不包含f2为null的任何行 - 因为在关系术语中,(NULL&lt;&gt;'OK')是NULL。但你的意图是返回所有不行的行。你需要一个额外的OR谓词,或者写一个来自'OK'的F2 DISTINCT(首先是特殊情况编码)。

IMO,NULL只是程序员工具中的一种,如指针算法或运算符重载,需要尽可能多的科学艺术。

Joe Celko在SQL For Smarties中写到这一点 - 在应用程序中使用NULL的陷阱是它的含义是,未定义。它可能意味着未知,未初始化,不完整,不适用 - 或者在上面的愚蠢例子中,它是否意味着好还是不好?

答案 7 :(得分:4)

感谢所有伟大的答案,伙计们。你给了我很多思考,帮助我形成了自己的观点/策略,归结为:

  

if-and-only-null如果为null,则允许空值   该列将具有特定的   对你的申请意义重大。

null的几个常见含义:

  • 直接来自用户的任何内容
    • 此处null表示“用户未输入”
    • 对于这些列,最好允许空值,或者你只需​​输入 asdasd@asd.com 类型输入。
  • “0或1”关系的外键
    • null表示“无相关行”
    • 因此,允许这些列的空值
    • 这个是controversial,但这是我的意见。

通常,如果您无法在列中考虑null的有用含义,则它应为NOT NULL。您可以随时将其更改为可为空。

我最终得到的事情的例子:

create table SalesOrderLine (
    Id int identity primary key,
    -- a line must have exactly one header:
    IdHeader int not null foreign key references SalesOrderHeader, 
    LineNumber int not null, -- a line must have a line number
    IdItem int not null, -- cannot have null item
    Quantity decimal not null, -- maybe could sell 0, but not null
    UnitPrice decimal not null, -- price can be 0, but not null
    -- a null delivery address means not for delivery:
    IdDeliveryAddress int foreign key references Address, 
    Comment varchar(100), -- null means user skipped it
    Cancelled bit not null default (0) -- true boolean, not three-state!
    Delivered datetime, -- null means not yet delivered
    Logged datetime not null default (GetDate()) -- must be filled out
)

答案 8 :(得分:2)

我倾向于同意dorfier。

在您的应用程序中认真对待在接收数据库NULL值并将其视为空值时保持灵活性,并且您可以给自己很大的灵活性,让NULL插入您未指定的值。

在很多情况下,您需要一些非常严重的数据完整性(和/或禁止NULL字段的高速优化),但我认为这些问题可以抵消确保每个字段都需要的额外工作量。默认值和/或设置为合理的值。

答案 9 :(得分:2)

在所有事情上坚持使用NOT NULL,直到某人因为痛苦而发出吱吱声。然后尽可能不情愿地一次将其移到一列上。尽可能多地避免数据库中的空值,只要你可以。

答案 10 :(得分:2)

我个人认为你应该根据它们包含的数据类型将列标记为空或非空,如果真正要求数据始终存在,以及数据是否始终在输入。当用户没有数据时将列标记为非空将强制构成使您的所有数据无用的数据(这就是您最终获得垃圾数据的方式,例如包含“thisissilly@Ihatethisaplication.com”的电子邮件字段“)。没有要求必须在那里工作的东西(比如显示客户订单的关键字段)同样是愚蠢的。 Null an not null是一个数据完整性问题的核心,做最有意义的保持数据可用。

答案 11 :(得分:1)

如果您可以考虑长期,则列中包含NULL会影响您设计查询的方式。无论您使用CASE语句,COALESCE还是必须显式测试NULL值,都可以为您做出决定。

从性能角度来看,不必担心NULLS会更快。从设计的角度来看,使用NULL是一种简单的方法,可以知道项目从未填写过。有用的示例包括“UpdatedDateTime”列。 NULL表示项目从未更新过。

我个人在大多数情况下允许NULL。

答案 12 :(得分:1)

  

小型与大量NOT NULL列的性能影响是什么?

这可能是明显的,但是,当列可以为空时,每个记录将需要1个额外的存储位。因此,当 BIT 列可以为空时,它会消耗100%以上的存储空间,而 UNIQUEIDENTIFIER 在可以为空时只消耗0.8%的存储空间。

在病态情况下,如果您的数据库有一个由单个BIT列组成的表,那么使该列可以为空的决定会将数据库的性能降低一半。但是,在绝大多数现实场景下,可空性不会对性能产生可测量的影响。

答案 13 :(得分:0)

使用'Not Null'或'Null'应主要由您的特定持久性要求驱动。

值为Nullable意味着有两个或三个状态(具有位字段的三个状态)

例如;如果我有一个名为'IsApproved'的位字段,并且该值设置在插入后的阶段。然后有三种状态:

  1. 'IsApproved'未解答
  2. 'IsApproved'已获批准
  3. 'IsApproved'未获批准
  4. 因此,如果某个字段可以被合法地视为未回答,并且没有适合的默认值。这些字段应该被认为是可以为空的

答案 14 :(得分:-1)

任何可以为空的列都违反了第三范式。

但是,这不是答案。

可能是这样的:数据库中有两种类型的列 - 包含数据的结构的列,以及包含数据的内容的列。键是结构,用户可输入的字段是数据。其他事情 - 好吧 - 这是一个判断电话。

在join子句中使用的结构通常不为null。这个数据通常可以为空。

如果您的列中包含一个选项列表或null(没有选择),那么为“无选择”而不是可为空的列设置特定值通常是个好主意。这些类型的列通常参与连接。