【问题标题】:Should NULLS be handled in code or in the database? Advantages and Disadvantages?NULLS 应该在代码中还是在数据库中处理?的优点和缺点?
【发布时间】:2010-12-20 01:02:33
【问题描述】:

我有几个关于在哪里处理空值的问题。让我设置一个场景。想象一下,我有一个包含 5 个 varchar(50) 列的表,在提供使用 null 或空字符串的原因时用作示例。

  1. 在代码中还是在数据库中处理 NULLS 更好?我的意思是,如果 varchar(50) 不包含任何值,最好将空字符串分配给它,还是将 null 分配给 varchar(50) 并在代码中处理该 null 更好?

    李>
  2. 将空字符串分配给列会影响性能开销吗?

  3. 使用 null 与空字符串对索引有何影响?

  4. 我的印象是,如果您不允许数据库包含空值,则不必在代码中处理它。这个说法是真的吗?

  5. 在使用默认值时,除了 varchars 之外的其他数据类型是否会造成同样的问题,还是使用字符串数据类型时问题更大?

  6. 如果表包含空值,使用 ISNULL 函数的开销是多少?

  7. 还有哪些其他优点/缺点?

【问题讨论】:

  • 我认为在您的问题 1) 中,您的“null”之一需要更改为“空字符串”。
  • 在紧凑的框架上,我发现 ISNULL 速度慢到无法使用的地步。在桌面上,它既快速又方便。
  • 为什么这是一个社区维基?对我来说,这似乎是一个非常有效的问题。
  • 我把它做成了一个wiki,因为我认为你的数据库中是否有空字符串和空字符串可能是主观的,没有错误/正确的答案。
  • 所有设计问题都是主观的,因为我们从来没有获得足够的具体信息来得出客观的答案;-)

标签: c# sql-server tsql database-design


【解决方案1】:

我的一般建议是将数据库中的字段声明为 NOT NULL,除非您特别需要允许 null 值,因为它们对于数据库新手来说往往很难处理。

请注意,空字符串和空字符串字段的含义不一定相同(除非您将它们定义为)。通常 null 表示“未知”或“未提供”,而空字符串就是这样,一个已提供且已知的空字符串。

允许或禁止null 字段完全取决于您的需要。

【讨论】:

  • 我不同意您避免空值的建议,因为刚接触数据库的人会感到困惑。与其避免空值,不如避免将您的生产数据库的访问权限授予数据库新手!
  • 我同意您应该尽可能将字段声明为 NOT NULL,但我不同意您应该简化数据库设计以方便数据库新手。如果 NULL 是正确的选择,请使用 NULL。如果一些新程序员不理解 NULL,请向他们展示手册。
【解决方案2】:

主要优点是您可以在 .NET 和 SQL 代码中分别处理 null 和空字符串 - 毕竟它们可以表示不同的东西。

缺点是你需要小心;在 .NET 中,您不必在 null 上调用 obj.SomeMethod(),而在 SQL 中,您需要注意 null 在组合时往往会传播(例如,与 C# 字符串连接不同)。

null 和 empty 之间并没有明显的大小差异。在 .NET 代码中,我希望它使用内部的空字符串,但这并不重要。

【讨论】:

  • 马克,你能详细说明一下吗?
  • 另外,你是说它的主要优点是在数据库中使用空值吗?只是想澄清一下。
  • 究竟是什么?我是说优点是 null 和 blank 在两个层都是不同的。如果您的意思是 null,请使用 null。如果您的意思是空字符串,请使用空字符串!两者不一样。
  • 好的,假设您将两者视为相同。有性能优势/劣势吗?如果您一开始不接受数据库中的空值,您是否必须在应用程序中检查空值?
【解决方案3】:

NULL 存储效率更高(NULL 位图),然后是空字符串(varchar 长度为 2 个字节,char 为“n”)

Storage engine blog: Why is the NULL bitmap in a record an optimization?

我看过一些说不同的文章,但是对于 char/varchar,我发现 NULL 很有用,并且倾向于将空字符串与 NULL 一样对待。我还发现 NULL 在查询中也比空字符串更快。当然是 YMMV,我会根据每个案例的优点来评估每个案例。

【讨论】:

    【解决方案4】:

    您将实现问题与逻辑数据架构问题混合在一起。

    您应该完全根据字段是否准确地建模您希望存储在数据库中的数据来决定是否允许在字段中使用空值。正如其他一些人所指出的,部分混淆是空字符串和空字符串不仅仅是存储相同信息的两种方式。

    Null 表示没有值或值未知。
    空字符串意味着有一个值,它是一个空字符串。

    让我用一个例子来演示。例如,假设您有一个中间名字段,并且需要区分未填充中间名和该人没有中间名的情况。使用空字符串表示没有中间名,使用null表示未输入。

    在几乎所有情况下,在数据方面 null 有意义的情况下,它们应该在应用程序代码中处理,而不是在数据库需要区分两种不同状态的假设下在数据库中处理。

    简短版:不要根据数据库中的性能/存储问题选择空字符串和空字符串,选择最能模拟您尝试存储的信息的字符串。

    【讨论】:

    • “短版”下的最终评论本身就值得一票。
    【解决方案5】:

    我认为空值和空字符串在代码和数据库中是两种不同的东西。一个变量或字段为 null 意味着它没有值,但如果其中一个是空字符串,它确实有一个值恰好是空字符串。

    【讨论】:

      【解决方案6】:

      1:非常主观,正如其他答案所指出的那样,NULL(无答案/未知)和“”(已知为无/不适用 - 即没有中间名的人)之间存在明显差异。

      2:不应该这样做。

      3:AFAIK(我还是一名初级/正在学习的 DBA,所以对此持保留态度),但应该没有效果。

      4:这是有争议的。理论上,如果您将 NOT NULL 约束应用于数据库字段,那么您永远不必处理 NULL 值。在实践中,理论与实践的差距在理论上比在实践中要小。 (换句话说,即使理论上不可能,您也应该处理被赋予 NULL 的问题。)

      【讨论】:

      • 外连接查询使得仍然可以从查询返回空值。应使用 ifnull 或应在代码中处理 null 值。
      【解决方案7】:

      我通常在设计过程中默认使用 NOT NULL,除非另有说明 - 特别是会计中的货币/小数列 - 通常从来没有未知的方面。在某些情况下,货币列是可选的(例如调查或业务关系系统,您将家庭/企业收入放在其中 - 这可能直到/如果由客户经理形成关系才知道)。例如,对于日期时间,我永远不会允许 NULL RecordCreated 列,而 BirthDate 列将允许 NULL

      NOT NULL 列删除了许多潜在的额外代码,并确保用户不必通过特殊处理来考虑 NULLs - 特别适用于表示层视图或用于报告的数据字典。

      我认为在设计期间投入大量时间处理数据类型(char 与 varchar、nchar 与 nvarchar、money 与 decimal、int 与 varchar、GUID 与 identity)、NULL 是很重要的/NOT NULL、主键、聚集索引和非聚集索引的选择以及 INCLUDE 列。我知道这可能听起来像是 DB 设计中的所有内容,但如果事先了解所有这些问题的答案,您将拥有一个更好的概念模型。

      请注意,即使在不允许列为NULL 的数据库中,视图中的LEFT JOIN 也可能导致NULL

      对于决策过程的具体案例,让我们以 Address1、Address2、Address3 等所有 varchar(50) 的简单案例为例 - 一个非常常见的场景(可能更好地表示为单个 TEXT 列,但让我们假设它是这样建模的)。我不会允许 NULL,我会默认为空字符串。这样做的原因是:

      1) 这不是真的未知 - 它是空白的。多列之间的 UNKNOWN 的性质永远不会被明确定义。您极不可能有一个已知地址 1 和一个未知地址 2 - 您要么知道整个地址,要么不知道。除非你有约束,否则让它们为空并且不允许 NULL。

      2) 一旦人们开始天真地做诸如 Address1 + @CRLF + Address2 之类的事情 - NULL 就开始指向 NULL 整个地址!除非您打算用ISNULL 将它们包装在视图中,或者更改您的 ANSI NULL 设置,否则为什么不让它们为空 - 毕竟,这是用户查看它们的方式。

      对于中间名或中间名首字母,我可能会使用相同的逻辑,具体取决于它的使用方式 - 没有中间名的人和不知道中间名的人之间有区别吗?

      在某些情况下,我什至可能不允许空字符串 - 我会通过约束来做到这一点。例如 - 患者的名字和姓氏,客户的公司名称。这些不应该是空白或空的(或所有空格或类似的)。这些约束越多,您的数据质量就越好,您越早发现一些愚蠢的错误,例如导入问题、NULL 传播等。

      【讨论】:

      • NULL 传播是在表达式中使用 NULL 时(没有包装逻辑) - 表达式导致 NULL。这可能很微妙。例如,当存在只有 col1 或 col2 为 NULL 的行时,SUM(t.col1 + t.col2) 与 SUM(t.col1) + SUM(t.col2) 不同。因此,如果允许使用 NULL,您需要将它们包装起来以生成默认值,或者根据您的应用程序要求适当地考虑它们。即在应用程序术语中,仅仅因为数据库术语中的某些内容是未知的并不意味着应用程序术语中的结果是未知的。
      【解决方案8】:

      在数据库中放置伪造的数据(字符串数据为空字符串,数字为 0,日期为一些可笑的日期)而不是 null 几乎总是一个糟糕的选择。那些伪造的值并不意味着同样的事情,特别是对于数字数据,很难获得与真实值不同的伪造值。而且,当您输入不良数据时,您仍然需要围绕它编写代码以确保正确处理事情(例如不返回那些没有结束日期的记录),因此您实际上在开发方面没有任何保存。

      如果您在插入记录时无法知道数据,则 null 是最佳选择。也就是说,如果数据总是已知,则尽可能使用 not null。

      【讨论】:

        【解决方案9】:

        你应该研究一下第六范式。 6NF 是专门为摆脱使用 NULLS 引入的问题而发明的。 SQL 的三值逻辑(真、假、未知)以及程序员对二值逻辑的普遍使用使很多问题变得更糟。

        在 6NF 中,每次必须将行/列交叉点标记为 NULL,可以通过简单地省略行来处理这种情况。

        但是,我一般不会在数据库设计中尝试 6NF。大多数时候,NULLable 列不用作搜索条件或连接条件的一部分,并且 NULLS 的问题不会出现。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-01-14
          • 1970-01-01
          • 1970-01-01
          • 2019-05-02
          • 2011-02-20
          • 1970-01-01
          • 2020-10-24
          相关资源
          最近更新 更多