假设我们有如下表格:
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))
);
的 LiveDemo
数据:
SELECT * FROM dbo.tab;
╔══╦═════════╦══════════╦══════════╦══════════╦══════════╦══════════╦══════════╗
║id║is_active║calc_flag1║calc_flag2║calc_flag3║calc_flag4║calc_flag5║calc_flag6║
╠══╬═════════╬══════════╬══════════╬══════════╬══════════╬══════════╬══════════╣
║ 1║ NULL ║ False ║ False ║ 0 ║ False ║ 0 ║ False ║
║ 2║ True ║ True ║ True ║ 1 ║ True ║ 1 ║ True ║
║ 3║ False ║ False ║ False ║ 0 ║ False ║ 0 ║ False ║
╚══╩═════════╩══════════╩══════════╩══════════╩══════════╩══════════╩══════════╝
元数据检查:
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)
与之前相同,但明确ISNULL(is_active, 0)
。现在它应该工作,因为有硬编码的值和ISNULL
,但事实并非如此。
,calc_flag3 AS IIF(is_active IS NULL,0 , ISNULL(is_active,0))
这很有趣,没有CAST
它会得到nullable
- no
,但数据类型现在是INT
。
第三次尝试:
,calc_flag4 AS CAST(ISNULL(IIF(is_active IS NULL,0 , is_active), 0) AS BIT)
第二个值被硬编码时强制转换ISNULL
。 为什么这可以是nullable
?
,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))
现在我得到了正确的数据类型和可空性,但它有点难看。
问题在于,当使用calc_flag2
或calc_flag4
时,它的行为方式无法获取正确的元数据。
答案 0 :(得分:2)
根据MSDN,...
除非另有说明,否则计算列是未物理存储在表中的虚拟列。每次在查询中引用它们时,都会重新计算它们的值。
数据库引擎根据使用的表达式自动确定计算列的可为空性。即使只存在不可存在的列,大多数表达式的结果也被认为是可空的,因为可能的下溢或溢出也会产生空结果。使用带有AllowNull属性的COLUMNPROPERTY函数可以调查表中任何计算列的可为空性。 可以通过指定ISNULL(check_expression,constant)将可以为空的表达式转换为不可为空的表达式,其中常量是替换任何空结果的非空值。
所以我要说,因为你的is_active字段是可空的,所以引擎会计算出在最后的ISNULL中特别防范它们之前仍然可能达到空状态。
我将尝试创建一个溢出或下溢,在插入时导致位列为null,以便验证引擎,但您的问题似乎有效,因为您在计算列中的表达式特别保护使用案例陈述。
答案 1 :(得分:1)
首先要注意的是,当使用IIF
(在幕后扩展为CASE
表达式)时,如果全部,结果将只能为空返回表达式不可为空,因此当您使用:
IIF(is_active IS NULL,0,is_active)
虽然逻辑上当你在表达式中找到is_active
为false时,由于条件设置,它永远不会为null,这与编译器无关,它只能看到返回的表达式之一是{{ 1}}这是一个可以为空的列,因此返回的类型是可空的。
我认为问题可以简化到为什么ISNULL(is_active,0)产生一个不可为空的位列,但只是添加一个转换,如CONVERT(BIT,ISNULL(is_active,0)),导致同一列为空。
快速演示:
is_active
其中给出了
的相关结果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';
使用this answer中的特定部分(Credit to Paul White),原因是某些设置会话可能导致转换溢出返回null,因此确保不可为空的列的唯一方法是最外面的函数是Column_name Type Computed Nullable
--------------------------------------------
id int no no
is_active bit no yes
calc_flag1 bit yes no
calc_falg2 bit yes yes
。
如上所示,可以使用ISNULL
简单地实现所需的解决方案,因为这会返回一个不可为空的位列,但值得注意的是,如果需要转换,例如,如果您需要它一个int列,然后转换必须在ISNULL(is_active, 0)
内。由于ISNULL
将返回第一个参数的类型,因此只需要进行一次转换,例如
ISNULL
其中给出了
的相关结果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';