NULL有数据类型吗?

12

今天我遇到了类似于这样的代码。

SELECT AuditDomain, 
    ObjectId,
    AuditSubdomain = CONVERT(VARCHAR(50), NULL),
    SubDomainObjectId = CONVERT(INT, NULL)
FROM Audit

它似乎意味着数据类型信息可以与NULL值关联。这是否会将元数据附加到NULL值上,标识它为指定的数据类型?

这篇文章详细介绍了在Sql Server中查找数据类型的方法,但是当我尝试以下行时,它返回为NULL:

SELECT CAST(SQL_VARIANT_PROPERTY(CONVERT(INT, NULL), 'BaseType') AS VARCHAR(20))

你尝试过谷歌搜索“null数据类型”吗?你可能会发现它不是一种数据类型...而上面的代码之所以能够工作,是因为varchar可以为空。 - RandomUs1r
6个回答

18

在SQL Server中,NULL在我能想到的所有场景中默认为INT。 您可以使用以下代码确定此内容:

SELECT x = NULL INTO #x;
EXEC tempdb..sp_columns '#x';

结果:

TABLE_QUALIFIER TABLE_OWNER TABLE_NAME COLUMN_NAME DATA_TYPE TYPE_NAME
--------------- ----------- ---------- ----------- --------- ---------
tempdb          dbo         #x___...   x           4         int
在将数据放入表格或与某些上下文元数据相关联之前,这能为你带来什么好处?它是INT还是DATETIME或其他类型对你有何影响?你会如何处理这些信息? SQL_VARIANT_PROPERTY返回NULL,因为它似乎需要元数据和值两者才具有意义。观察以下示例(使用不同的类型以进行混合):
SELECT SQL_VARIANT_PROPERTY(NULL, 'BaseType');

DECLARE @x DATE;

SELECT SQL_VARIANT_PROPERTY(@x, 'BaseType');

DECLARE @y DATE = SYSDATETIME();

SELECT SQL_VARIANT_PROPERTY(@y, 'BaseType');

结果:

NULL

NULL

date

因此,似乎需要同时具有类型值才能准确地确定基本类型。

至于为什么会这样工作,耸肩。你得问那些有源代码访问权限的人。

请注意,只有在你强制要求SQL Server采用它时,NULL才必须采用基本类型:你已经创建了一个基于它的表。实际上,在这种情况下(以及许多情况下,SQL Server必须猜测你意味着什么数据类型),SQL Server可能会返回错误。避免这种情况的方法是不要创建让SQL Server去猜测的情况(这就是为什么我问你要用这些信息做什么的原因)。


你知道为什么sql_variant_property函数返回NULL吗? - ErikE
1
这是与UNION一起使用的。两个表的模式不同,当第二个表缺少列时,它们会放置这些CONVERT(<DataType>, NULL)语句。我运行了多个测试,其中我使用NULL而不是转换,一切都按预期工作。也许这是对UNION和/或类型转换工作方式的误解。 - Jason
@Jason 我敢打赌,如果没有进行转换,只使用 NULL 也可以正常工作,但这可能取决于 UNION 中的第一个表是否有缺失列。 - Aaron Bertrand
反例(即使使用UNION):SELECT CAST(NULL AS int) UNION SELECT 'abc'SELECT NULL UNION SELECT 'abc'。前者失败是因为int的优先级高于varchar,所以对于从'abc'到类型int的无效转换报告错误。后者成功了,所以显然没有将NULL视为具有类型int。然而,UNION的语义确实指定了所有子查询中所有列的类型。 - user743382
1
我至少可以想到两种反向情况:(i) SELECT DATEADD(day, NULL, CURRENT_TIMESTAMP) 失败;它默认拒绝将 null 视为整数 (ii) 在没有明确数据类型的情况下,SELECT MIN(x) FROM (VALUES (NULL)) AS v(x) 会失败。在我看来,SELECT INTO 的行为是不一致的。 - Salman A

1

当然,NULL有数据类型。 尝试下面的代码进行确认:

SELECT 
  CAST(NULL AS date) AS c1,
  CAST(NULL AS time) AS c2
INTO #x;
EXEC tempdb..sp_columns '#x';

我认为 SQL_VARIANT 实现中存在错误,因为它丢失了关于 NULL 类型的信息。太糟糕了!


0

我不同意@aaron-bertrand的观点。因为默认情况下,SQL Server创建的列使用整数数据类型来存储NULL值,因为NULL可以插入到Int、Datetime、Date、Varchar或其他任何列中。

当在表中插入Null时,例如SELECT x = NULL INTO #x;

SQL Server默认创建整数类型列,因为NULL可以插入到整数列中。

所以可能是由于我们编写的查询语句"SELECT x = NULL INTO #x;"的性质导致了该列成为整数并放置了NULL值。


0

0

我知道你已经有了一些好的答案,但我认为 SQL_VARIANT_PROPERTY 被误解了。

你可以使用 SQL_Variant_Property 与一个列一起使用,并指示你想要它的元数据属性。然而,如果它是空的,它并不能告诉你太多信息。

例如:

declare 
    @Start date = getdate()
,   @End datetime= getdate()
,   @Int int = 1
,   @DateNull date
;


select 
    sql_variant_property(@Start, 'BaseType')
,   sql_variant_property(@End, 'BaseType')
,   sql_variant_property(@Int, 'BaseType')
,   sql_variant_property(@DateNull, 'BaseType')

将返回三种数据类型和一个空值。处理 NULL 是 SQL 的重要部分。许多人,包括我自己,有时希望使用一个代表其的值来处理 null,或者在其他时候不关心。SQL 变量仅对填充了参数:“BaseType”的值有效,否则它会返回 null。据我所知,这是由于 SQL 说:“您这里没有数据,无法确定内存使用情况。”

通常,当我明确希望数据集包含未知值的零时,我会指定 isnull(@thing, 0)。其他时候,我可能希望用户知道空值发生了其他事情,例如 isnull(@thing,'not present') 用于报告。还有其他时候,您可以使用 coalesce 来基本上创建一系列可能性 coalesce(@thing, @otherthing, @yetotherthing, 'unknown')。

我认为在代码中,你看到的是有人在转换某些东西,但我不确定你真正需要这样做的地方。表中列的数据类型保持其所需的状态,当它为空时,SQL 不会在其中存储内存。因此,改变它的必要性在我看来似乎是武断的。我知道,当你预期有更多的空值时,可以使用 SQL 2008 中引入的 SPARSE 选项来处理更好的内存消耗。但我不知道什么时候将几乎不消耗任何东西的内容转换为更多的内容。


0

SQL Server 2022似乎有一个“NULL”数据类型。比较以下两个查询的结果。

SELECT SUM(v)
FROM (
    VALUES (NULL)
        ,(cast(NULL AS INT))
    ) AS t(v)

输出:NULL

SELECT SUM(v)
FROM (
    VALUES (NULL)
        ,(NULL)
    ) AS t(v)

输出:操作数数据类型 NULL 对于求和运算符无效。

第一个查询明确将 NULL 强制转换为 INT。这似乎表明,NULL 和 INT 都是数据类型,并且 INT 具有更高的优先级。但是,我从未在任何地方看到过这种情况的文档记录。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接