logo SQL Prompt教程 我也要发布文档

SQL语法提示工具SQL Prompt使用教程:为什么应该始终指定列是否接受空值


SQL Prompt是一款实用的SQL语法提示工具。根据数据库的对象名称、语法和代码片段自动进行检索,为用户提供合适的代码选择。自动脚本设置使代码简单易读--当开发者不大熟悉脚本时尤其有用。SQL Prompt安装即可使用,能大幅提高编码效率。此外,用户还可根据需要进行自定义,使之以预想的方式工作。

点击下载SQL Prompt正式版

在大多数情况下,需要指定列是否应允许空条目。依靠默认值并不是一个好主意,假设,如果不使用NULL或NOT NULL显式指定列的可为空性,则该列应为可为空。如果您不为给定的数据类型选择该选项,那么控制发生什么事情的规则很难解释,即使您了解这些规则,您的团队或后继者也会愿意这样做吗?

在SQL中,NOT NULL子句是“逻辑约束”,用于确保列永远不会获得分配给它的空值。相反,NULL子句清楚表明希望该列接受空值。如果您的表规范不包含这些子句,则从数据库属性、连接设置或数据类型的默认值确定是否存在空值。基本上,您并不总是知道,并且可以轻松地得到不允许为空的列。

如果在创建或更改表或声明表变量时未能指定列的可为空性,则SQL Prompt的最佳实践代码分析规则BP014会警告您(每个表达式一次违反一次)。


为什么要关心列是否为空?

之所以需要指定此名称,是因为关系数据库旨在有效地防止不良数据进入。约束是实现此目的的方法。因此,必须对不能合法包含null的所有列使用NOT NULL。 如果您指定一列为非空值,那么您将定义一个约束,以确保该列永远不会容纳或接受空值,因此您不能意外的将该值保留下来。通过在列中允许空值,除非使用ISNULL()、IFNULL()和NULLIF()之类的函数来处理空值,否则还使聚合变得棘手,并使WHERE子句产生意外结果。

如果不指定该列,则不要假设该列将允许为空

本文旨在证明为什么您应该始终指定在任何表中定义的列是否允许空值。不能深入讨论NOT NULL约束是否是一件好事,而只是解释了为什么需要声明自己的偏好。

您可能会认为,如果您在列的定义中未包含NOT NULL约束,那么该列将可以为空。不,错了。它可以为空,但也可能不是。它取决于数据类型、数据库设置和连接设置。除非您能记住所有规则并保证用于DDL脚本的连接类型,否则接受始终指定列为NULL或NOT NULL的单个规则要简单得多。

现在,我们将研究各种因素,这些因素可以决定一列是否得到NOT NULL约束(如果您未指定的话)。

您的数据库指定默认值

如果程序员在创建或更改表时未指定列的可空性,则数据库设置(特别是该ANSI_NULL_DEFAULT选项)将确定该列是NULL还是NOT NULL,除非您拥有覆盖该列的SQL Server连接或其他因素,我会解释,将其覆盖。

ANSI_NULL_DEFAULT选项是sql_option设置之一,语法为:

ALTER Database SET { database_name | CURRENT } SET ANSI_NULL_DEFAULT { ON | OFF }

如果设置为ON,则在发出CREATE TABLE或ALTER TABLE语句时,允许所有未显式定义为NOT NULL的用户定义数据类型或列为空值。

您可以通过以下方式查看当前数据库的设置:

SELECT DATABASEPROPERTYEX(Db_Name(), 'IsAnsiNullDefault');

返回1或0。

您的连接指定默认

您可以通过应用程序的连接设置覆盖数据库设置。所有常用的设置都可以做到这一点。您使用的连接通常会指定默认值应为NULL。这样做可能不是很明智,但它是ANSI标准,这表明如果将更通用的ANSI_DEFAULTS设置为ON,也会发现它还将ANSI_NULL_DFLT_ON设置为ON。

SSMS允许您指定用于连接到SQL Server的默认值,如果需要,可以覆盖ANSI标准。存在集ANSI_NULL_DFLT_OFF和集ANSI_NULL_DFLT_ON,尽管它们不能同时设置为ON。您可以选择将两者都关闭,在这种情况下,您只是选择继承数据库默认值,或者可以通过将ANSI_NULL_DFLT_ON设置为ON来坚持默认值是NULL。如果您愿意,可以通过将ANSI_NULL_DFLT_OFF设置为ON来覆盖数据库设置,以使默认值为 NOT NULL。

SET ANSI_NULL_DFLT_ON { ON | OFF }
SET ANSI_NULL_DFLT_OFF { ON | OFF }

SQLCMD、BCP和SSMS略有不同,但通常它们是一致的。连接时,SQL Server的SQL Server本机客户端ODBC驱动程序和SQL Server的SQL Server本机客户端OLE DB提供程序会自动设置ANSI_NULL_DFLT_ON为ON。但是,对于来自遗留的db库应用程序的连接,SET ANSI_NULL_DFLT_ON的默认设置是关闭的。

因此,如果我们想查看为连接启用了哪些设置,则可以运行…

SELECT Setting FROM (VALUES 
(1 , 'DISABLE_DEF_CNST_CHK'),
(2 , 'IMPLICIT_TRANSACTIONS'),
(4 , 'CURSOR_CLOSE_ON_COMMIT'),
(8 , 'ANSI_WARNINGS'),
(16 , 'ANSI_PADDING'),
(32 , 'ANSI_NULLS'),
(64 , 'ARITHABORT'),
(128 , 'ARITHIGNORE'),
(256 , 'QUOTED_IDENTIFIER'),
(512 , 'NOCOUNT'),
(1024 , 'ANSI_NULL_DFLT_ON'),
(2048 , 'ANSI_NULL_DFLT_OFF'),
(4096 , 'CONCAT_NULL_YIELDS_NULL'),
(8192 , 'NUMERIC_ROUNDABORT'),
(16384 , 'XACT_ABORT'))f(Bit,Setting)
WHERE bit & @@Options =bit

返回:


数据类型定义为NOT NULL

除非另外指定,否则Microsoft提供的几个数据类型(timestamp和sysname)都不为空。您可以根据系统数据类型指定自己的别名数据类型,并指定它们是否默认为可为空。要查看哪些数据类型不为空,可以使用如下查询:…

SELECT name FROM sys.types WHERE is_nullable=0

要使它们可为空或不可为空,请使用以下语法

CREATE TYPE [ schema_name. ] type_name 
{ 
 [
 FROM base_type 
 [ ( precision [ , scale ] ) ] 
 [ NULL | NOT NULL ]
 ]

您会看到这对于处理具有特定维度、含义或用途的数据是多么的方便。您可以引用一个姓氏数据类型,并知道它不可能NULL(尽管它可以为'null'),并且它的最大值可以在数据库中的一个地方更改,如果您突然发现它的长度不够。

对于基本类型为数字或十进制的数字数据,它也非常方便。这意味着出错和意外截断一个值要困难得多。除了精度和规模之外,您可以指定其默认为空。

这意味着用户别名类型是数据类型之一,通常最好不要通过覆盖可空性规范(如果存在)来尝试通过在基于此类型创建列时显式指定NULL来覆盖它。有人已经决定类型必须为NULL或NOT NULL,这可能是一个很好的理由。

列参与主键

如果您为主键分配一列,则为NOT NULL。让我们演示一下:

CREATE TABLE TestOutNullability
(
MyTimestamp TIMESTAMP,
ObjectName sysname,
MyInt INT,
MyCode NVARCHAR(120),
CONSTRAINT myPK PRIMARY KEY (MyInt,MyCode)
)

因此,让我们看看所有未指定可空性的列是否都将允许空值:

SELECT c.name, c.is_nullable FROM sys.tables AS T
INNER JOIN sys.columns AS C
ON C.object_id = T.object_id
WHERE t.name='TestOutNullability'
ORDER BY c.column_id

这些列均不可为空。我们可以通过使SSMS使用生成的构建脚本对表进行反向工程来证明这一点

/****** Object: Table [dbo].[TestOutNullability] Script Date: 07/02/2020 19:39:12 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TestOutNullability](
    [MyTimestamp] [timestamp] NOT NULL,
    [ObjectName] [sysname] NOT NULL,
    [MyInt] [int] NOT NULL,
    [MyCode] [nvarchar](120) NOT NULL,
 CONSTRAINT [myPK] PRIMARY KEY CLUSTERED 
(
    [MyInt] ASC,
    [MyCode] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

看这里!所有列上的NOT NULL约束,即使您的连接设置指定允许它们为默认值也是如此。

结论

我不打算讨论在表的列中使用允许空值是否是一个好主意。但是,可以肯定地说,通常应明确指定一列是否应允许它们。我会说“一般”,因为如果您使用的是用户定义的别名类型,则有一个参数可以忽略该参数,以便在数据库中使用该类型是一致的。为了安全起见,您需要确保已在创建别名类型的代码中指定了它!

您可能会在CREATE TABLE编写代码时理解该代码,以及在执行DDL代码时的连接状态,但是它是否可重复?继承您的代码的可怜人或必须阅读该代码的团队成员,会得到什么启发吗?他们会想要得到线索吗?

本文内容到这里就完结了,希望对您有所帮助~感兴趣的朋友可以下载SQL Prompt试用版免费体验!或者关注我们慧都网了解更多产品相关资讯~

相关内容推荐:

SQL Prompt 使用教程>>>


想要购买SQL Prompt正版授权,或了解更多产品信息请点击【咨询在线客服】