SQL Server计算列';s元数据
假设我们有如下表:SQL Server计算列';s元数据,sql,sql-server,tsql,sql-server-2012,calculated-columns,Sql,Sql Server,Tsql,Sql Server 2012,Calculated Columns,假设我们有如下表: CREATE TABLE dbo.tab(id INT PRIMARY KEY -- other columns ,is_active BIT); INSERT INTO dbo.tab(id, is_active) VALUES (1, NULL), (2, 1), (3,0); 这种情况是添加将NULL更改为0的计算列,并在可能的情况下返回原始值 最终结果: 原始列必须保持不变(因此
CREATE TABLE dbo.tab(id INT PRIMARY KEY
-- other columns
,is_active BIT);
INSERT INTO dbo.tab(id, is_active)
VALUES (1, NULL), (2, 1), (3,0);
这种情况是添加将NULL
更改为0
的计算列,并在可能的情况下返回原始值
最终结果:
- 原始列必须保持不变(因此不可能
和UPDATE
表)ALTER
- 计算列的类型和可空性必须正确
- 无法使用
VIEW/TRIGGER/…
CREATE TABLE dbo.tab(
id INT PRIMARY KEY
,is_active BIT
,calc_flag1 AS CAST(IIF(is_active IS NULL,0 ,is_active) AS BIT)
,calc_flag2 AS CAST(IIF(is_active IS NULL,0 ,ISNULL(is_active,0)) AS BIT)
,calc_flag3 AS IIF(is_active IS NULL,0 , ISNULL(is_active,0))
,calc_flag4 AS CAST(ISNULL(IIF(is_active IS NULL,0 , is_active), 0) AS BIT)
,calc_flag5 AS ISNULL(IIF(is_active IS NULL,0 ,is_active),0)
,calc_flag6 AS ISNULL(CAST(IIF(is_active IS NULL,0 ,is_active) AS BIT),
CAST(0 AS BIT))
);
数据:
和元数据检查:
EXEC sp_help 'dbo.tab';
╔═════════════╦══════╦══════════╦════════╦══════╦═══════╦══════════╗
║ Column_name ║ Type ║ Computed ║ Length ║ Prec ║ Scale ║ Nullable ║
╠═════════════╬══════╬══════════╬════════╬══════╬═══════╬══════════╣
║ id ║ int ║ no ║ 4 ║ 10 ║ 0 ║ no ║
║ is_active ║ bit ║ no ║ 1 ║ ║ ║ yes ║
║ calc_flag1 ║ bit ║ yes ║ 1 ║ ║ ║ yes ║
║ calc_flag2 ║ bit ║ yes ║ 1 ║ ║ ║ yes ║
║ calc_flag3 ║ int ║ yes ║ 4 ║ 10 ║ 0 ║ no ║
║ calc_flag4 ║ bit ║ yes ║ 1 ║ ║ ║ yes ║
║ calc_flag5 ║ int ║ yes ║ 4 ║ 10 ║ 0 ║ no ║
║ calc_flag6 ║ bit ║ yes ║ 1 ║ ║ ║ no ║
╚═════════════╩══════╩══════════╩════════╩══════╩═══════╩══════════╝
第一次尝试:
,calc_flag1 AS CAST(IIF(is_active IS NULL,0 ,is_active) AS BIT)
,calc_flag2 AS CAST(IIF(is_active IS NULL,0 ,ISNULL(is_active,0)) AS BIT)
正确的数据类型,但无法获取nullability。我可以理解,因为它有硬编码的值和可为null的列,所以整个表达式的计算结果都是可为null的
第二次尝试:
,calc_flag1 AS CAST(IIF(is_active IS NULL,0 ,is_active) AS BIT)
,calc_flag2 AS CAST(IIF(is_active IS NULL,0 ,ISNULL(is_active,0)) AS BIT)
与前面相同,但显式ISNULL(处于活动状态,0)
。现在它应该可以工作了,因为有硬编码值,ISNULL
,但它没有
,calc_flag3 AS IIF(is_active IS NULL,0 , ISNULL(is_active,0))
这很有趣,如果没有CAST
,它会得到null
-no
,但是现在数据类型是INT
第三次尝试:
,calc_flag4 AS CAST(ISNULL(IIF(is_active IS NULL,0 , is_active), 0) AS BIT)
,calc_flag6 AS ISNULL(CAST(IIF(is_active IS NULL,0 ,is_active) AS BIT),
CAST(0 AS BIT))
当第二个值为硬编码时,强制转换ISNULL
为什么这可以为空
?
,calc_flag5 AS ISNULL(IIF(is_active IS NULL,0 ,is_active),0)
当然,不铸造它的作品,因为它应该
最后一次尝试:
,calc_flag4 AS CAST(ISNULL(IIF(is_active IS NULL,0 , is_active), 0) AS BIT)
,calc_flag6 AS ISNULL(CAST(IIF(is_active IS NULL,0 ,is_active) AS BIT),
CAST(0 AS BIT))
现在我得到了正确的数据类型和可空性,但它有点难看
问题是,当使用
calc_-flag2
或calc_-flag4
时,它为什么会这样做,并且无法获得正确的元数据 根据MSDN
除非另有规定,否则计算列是物理上未存储在表中的虚拟列。每次在查询中引用它们时,都会重新计算它们的值
数据库引擎根据使用的表达式自动确定计算列的可空性。即使只存在不可为null的列,大多数表达式的结果也被视为可为null,因为可能的下溢或溢出也会产生null结果。将COLUMNPROPERTY函数与AllowsNull属性一起使用,以调查表中任何计算列的可空性通过指定ISNULL(检查表达式,常量),可以将可为null的表达式转换为不可为null的表达式,其中常量是替换任何null结果的非null值。
所以我想说,由于您的is_active字段是可以为null的,所以引擎计算仍然有可能达到null条件,直到您在最终的ISNULL中专门防范它们
我将尝试创建一个溢出或下溢,在插入时导致位列为null,以验证引擎,但您的问题似乎是有效的,因为您在计算列中的表达式专门使用case语句。首先要注意的是,当使用
IIF
(在幕后扩展为CASE
表达式),仅当所有返回表达式不可为空时,结果才可为空,因此使用时:
IIF(is_active IS NULL,0,is_active)
虽然从逻辑上讲,当您在false的表达式中到达is\u active
时,由于设置了条件,它将永远不会为null,但这与编译器无关,它只能看到其中一个返回的表达式是is\u active
,这是一个可为null的列,因此返回的类型是可为null的
我认为问题可以简化为为什么ISNULL(is_active,0)产生一个不可为null的位列,而简单地添加一个类似convert的convert(bit,ISNULL(is_active,0))会导致同一列为null
快速演示:
CREATE TABLE #tab(
id INT PRIMARY KEY
,is_active BIT
,calc_flag1 AS ISNULL(is_active, 0)
,calc_falg2 AS CONVERT(BIT, ISNULL(is_active, 0))
);
EXECUTE tempdb.dbo.sp_help '#tab';
它给出了相关的结果
Column_name Type Computed Nullable
--------------------------------------------
id int no no
is_active bit no yes
calc_flag1 bit yes no
calc_falg2 bit yes yes
Column_name Type Computed Nullable
--------------------------------------------
id int no no
is_active bit no yes
calc_flag1 bit yes no
calc_falg2 bit yes yes
calc_falg_int int yes no
使用(Credit to)的特定部分的原因是,某些设置会话可能会导致转换溢出返回null,因此确保不可为null的列的唯一方法是如果最外层的函数是ISNULL
只需使用ISNULL(is_active,0)即可获得所需的解决方案
如上所示,由于这将返回不可为空的位列,但值得注意的是,如果需要转换,例如,如果需要将其转换为int列,则转换必须在ISNULL
内。由于ISNULL
将返回第一个参数的类型,因此只需要一次转换,例如。g
CREATE TABLE #tab(
id INT PRIMARY KEY
,is_active BIT
,calc_flag1 AS ISNULL(is_active, 0)
,calc_falg2 AS CONVERT(BIT, ISNULL(is_active, 0))
,calc_flag_int AS ISNULL(CONVERT(INT, is_active), 0)
);
EXECUTE tempdb.dbo.sp_help '#tab';
它给出了相关的结果
Column_name Type Computed Nullable
--------------------------------------------
id int no no
is_active bit no yes
calc_flag1 bit yes no
calc_falg2 bit yes yes
Column_name Type Computed Nullable
--------------------------------------------
id int no no
is_active bit no yes
calc_flag1 bit yes no
calc_falg2 bit yes yes
calc_falg_int int yes no
好奇……我投票决定关闭并迁移到dba站点,希望Paul White或在SQL Server内部具有类似专业知识的人能看到它。值得一提的是,您可以使用简单的
calc\u flag7作为ISNULL(is\u active,0)
获得一个不可空位列,我认为问题可以简化为为什么ISNULL(is_active,0)
生成一个不可为空的位列,但只需添加一个类似于convert(bit,ISNULL(is_active,0))
的转换即可使同一列为空。使用(我知道Paul White会有答案!)原因是某些设置会话可能会导致转换溢出返回null,因此确保不可为null的列的唯一方法是如果最外层的函数是ISNULL
。另一个注意事项是,如果所有返回表达式都不可为null,则CASE
表达式将只有不可为null的返回类型,因此即使使用IIF(is\u active is NULL,0,is\u active)
时,false的返回表达式将永远不会到达,如果它为NULL,它仍然是一个可为NULL的返回类型,因此返回的类型是可为NULL的。@GarethD是的,现在它有了完美的含义。感谢您的解释和链接。您会编译答案吗?