SQL Server 的 is_nullable 有什么意义吗?

Eva*_*oll 1 null sql-server datatypes metadata sql-server-2019

SQL Server 有一个类型标志,称为is_nullable您可以在 上看到它sys.types。目前,(SQL Server 2019)它只设置为FALSE两种类型,sys.timestampsys.sysname. 对sys.timestamp这种类型似乎接受null。在sys.sysname它没有。这种行为有什么解释吗?

你可以看到这些类型

SELECT * FROM sys.types WHERE is_nullable = 0;  
SELECT TYPEPROPERTY('timestamp', 'AllowsNull');  -- returns 0
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它,

CREATE TABLE foo ( a sys.timestamp );
INSERT INTO foo (a) VALUES ( null );
Run Code Online (Sandbox Code Playgroud)

请注意,如果您使用,这将不起作用 sys.sysname

Msg 515 Level 16 State 2 Line 1
Cannot insert the value NULL into column 'a', table 'dbo.foo'; column does not allow nulls. INSERT fails.
Msg 3621 Level 0 State 0 Line 1
The statement has been terminated.
Run Code Online (Sandbox Code Playgroud)

如果您创建用户定义的数据类型,它也将不起作用NOT NULL

CREATE TYPE bar FROM int NOT NULL;
Run Code Online (Sandbox Code Playgroud)

这只是对用户的提示吗?

Pau*_*ite 7

你的问题的答案是肯定的。

该列指定数据类型的默认可空性,如果用户没有表达偏好,例如在CREATE TABLE带有显式NULLNOT NULL规范的语句中,这可能很重要。遗憾的是,该信息仅存在于旧兼容性视图的文档中sys.systypes

允许空值(位)

指示此数据类型的默认可空性。如果使用CREATE TABLE或指定可空性,则此默认值将被覆盖ALTER TABLE。|

例如,可以覆盖NOT NULLsysname类型的默认行为:

DECLARE @T table (s sysname NULL);
INSERT @T (s) VALUES (NULL);
SELECT s FROM @T AS T;
Run Code Online (Sandbox Code Playgroud)
空值

timestamp类型(优选名称rowversion)具有附加的特殊行为由于其预期用途来检测行更改。有关更多详细信息,请参阅此其他答案。您仍然可以以相同的方式覆盖其可空性,但实际上不可能为时间戳/行版本属性存储 NULL。

有几个复杂的层来确定属性可空性。我不会在此处重复某些细节,而是向您推荐Phil Factor为什么您应该始终指定列是否接受空值以获取背景信息和其他阅读材料。

  • 这是我现在给你的最好的答案。如果不满意,我确实提供全额退款。 (6认同)

Tib*_*szi 5

让我重新表述这个问题,以确保我们在同一页面上:

时间戳类型的元数据表示它不接受 NULL,但如果您创建的表没有指定可空性,您可以在 INSERT 上为该列指定 NULL。为什么?

如果您从表中进行 SELECT,您将看到 INSERT 后该值不为空。您拥有生成的“时间戳”(或 rowversion,如果您喜欢该名称)。即,INSERT 上的 NULL 是没有意义的,这意味着使用了“默认”(可以这么说)。

可以讨论这个时间戳类型的元数据属性是否应该反映我们是否可以在 INSERT 上指定 null 或者我们最终是否可以在该列中实际包含NULL。

我想我们必须在 Sybase 几十年前设计系统表时在场才能知道答案。我不是。:-)

因此,简而言之:时间戳类型的列不允许表中为空,并且基于这一点可以认为元数据是正确的。

(顺便说一句,我首先认为这是一个向后兼容的事情,所以我启动了一个 6.5 版本的 SQL Server 进行测试。我几乎也启动了一个 4.2 版本。但后来我做了一个 SELECT 并且它不是'直到那时我才注意到该值根本不是 NULL。)

这是我使用的 T-SQL(在 2019 年),FWIW:

USE tempdb

DROP TABLE IF EXISTS myTable;
DROP TYPE IF EXISTS myTypeYesNull;
DROP TYPE IF EXISTS myTypeNoNull;

CREATE TYPE myTypeYesNull FROM int NULL;
CREATE TYPE myTypeNoNull FROM int NOT NULL;
GO

CREATE TABLE myTable (yesNull myTypeYesNull, noNull myTypeNoNull, ts timestamp, sname sysname);
GO

SELECT name, is_nullable FROM sys.types WHERE name IN ('myTypeYesNull', 'myTypeNoNull', 'timestamp', 'sysname')

SELECT 
 COLUMNPROPERTY(OBJECT_ID('myTable'), 'yesNull', 'AllowsNull') AS yesNull
,COLUMNPROPERTY(OBJECT_ID('myTable'), 'noNull', 'AllowsNull') AS noNull
,COLUMNPROPERTY(OBJECT_ID('myTable'), 'ts', 'AllowsNull') AS ts
,COLUMNPROPERTY(OBJECT_ID('myTable'), 'sname', 'AllowsNull') AS sname;

--The ts column accepts NULL even though table meta-data states differently
INSERT INTO myTable (yesNull, noNull, ts, sname)
VALUES(NULL, 1, NULL, 'x')

SELECT * FROM myTable

----------------------------------------------------------------------

--Code executable on really old versions
DROP TABLE myTable 
GO
SELECT name, allownulls FROM systypes WHERE name = 'sysname'
CREATE TABLE myTable(ts timestamp NULL)
GO

--Doesn't fail on 6.5
INSERT INTO myTable(ts) VALUES(null)
SELECT * FROM myTable
Run Code Online (Sandbox Code Playgroud)