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是的,现在它有了完美的含义。感谢您的解释和链接。您会编译答案吗?