不可空列的 UNION 可以为空
UNION of non-nullable columns is nullable
运行 MS SQL 服务器 2014 快递。突然,视图中的其中一列变成了可以为空的,尽管它是两个不可为空的列的并集。请帮助我了解这里发生了什么:
和联盟:
我错过了什么?
使用更多信息进行编辑: 当我重新保存 IncomingTransactions 视图时,它的列现在变为空,但不应该如此!下面是数量列的定义:(CASE PIN.StatusId WHEN 6 THEN PIN.QuantityReceived WHEN 7 THEN 0 ELSE PIN.QuantityRevised END) AS 数量。每个数量字段都是非空的,并且 case 语句是详尽无遗的。查询的其余部分是对 StatusId 字段的简单连接,它是一个非空 FK,所以我仍然迷失在这里。
编辑 2: 根据下面 YB 的建议,我创建了一个重现此行为的最小测试用例:
Create Table ybTest1 (Q1 decimal (7,2) not null, X int not null);
GO
Create View ybTestNSB As
Select (CASE X WHEN 0 THEN Q1 ELSE CAST(0 as decimal(7,2)) END) AS Q From ybTest1
GO
ybTest1 视图中的 Q 列为空,即使 case 语句是详尽无遗的。即使我按照 YB 的建议用 CAST(0 as decimal(7,2))
将 ELSE 分支中的 0 包装起来,它仍然是空的。要么 CASE 没有我想的语义,要么这是一个错误。
几乎每个作为表达式结果计算的列在 SQL 服务器中都被视为可以为空。解决方法是像您一样使用 ISNULL。这在 computed columns section 这里提到
The Database Engine automatically determines the nullability of
computed columns based on the expressions used. The result of most
expressions is considered nullable even if only nonnullable columns
are present ... An expression that is nullable can be turned into a
nonnullable one by specifying ISNULL(check_expression, constant)
,
where the constant is a nonnull value substituted for any null result.
但它适用于作为计算结果派生列的任何地方,包括在视图定义中。
几乎没有或根本没有逻辑来分析 null 是否真的可能(有时比看起来更难,因为各种不推荐使用的设置选项会导致 null 而不是溢出错误,所以即使 1 + X
也可能在您的示例)并且它在谨慎方面犯了错误。我看不到您的 case
表达式实际上可以输出 null
的任何方式,但根据我的经验,几乎所有计算列都将被视为可为空,但包裹在 isnull
中的列除外。
所以在你的测试用例中你可以替换
Create View ybTestNSB As
Select (CASE X WHEN 0 THEN Q1 ELSE CAST(0 as decimal(7,2)) END) AS Q From ybTest1
与
Create View ybTestNSB As
Select ISNULL(CASE X WHEN 0 THEN Q1 END, 0) AS Q From ybTest1
为了避免在此处放置令人讨厌的完全多余的表达式。
运行 MS SQL 服务器 2014 快递。突然,视图中的其中一列变成了可以为空的,尽管它是两个不可为空的列的并集。请帮助我了解这里发生了什么:
和联盟:
我错过了什么?
使用更多信息进行编辑: 当我重新保存 IncomingTransactions 视图时,它的列现在变为空,但不应该如此!下面是数量列的定义:(CASE PIN.StatusId WHEN 6 THEN PIN.QuantityReceived WHEN 7 THEN 0 ELSE PIN.QuantityRevised END) AS 数量。每个数量字段都是非空的,并且 case 语句是详尽无遗的。查询的其余部分是对 StatusId 字段的简单连接,它是一个非空 FK,所以我仍然迷失在这里。
编辑 2: 根据下面 YB 的建议,我创建了一个重现此行为的最小测试用例:
Create Table ybTest1 (Q1 decimal (7,2) not null, X int not null);
GO
Create View ybTestNSB As
Select (CASE X WHEN 0 THEN Q1 ELSE CAST(0 as decimal(7,2)) END) AS Q From ybTest1
GO
ybTest1 视图中的 Q 列为空,即使 case 语句是详尽无遗的。即使我按照 YB 的建议用 CAST(0 as decimal(7,2))
将 ELSE 分支中的 0 包装起来,它仍然是空的。要么 CASE 没有我想的语义,要么这是一个错误。
几乎每个作为表达式结果计算的列在 SQL 服务器中都被视为可以为空。解决方法是像您一样使用 ISNULL。这在 computed columns section 这里提到
The Database Engine automatically determines the nullability of computed columns based on the expressions used. The result of most expressions is considered nullable even if only nonnullable columns are present ... An expression that is nullable can be turned into a nonnullable one by specifying
ISNULL(check_expression, constant)
, where the constant is a nonnull value substituted for any null result.
但它适用于作为计算结果派生列的任何地方,包括在视图定义中。
几乎没有或根本没有逻辑来分析 null 是否真的可能(有时比看起来更难,因为各种不推荐使用的设置选项会导致 null 而不是溢出错误,所以即使 1 + X
也可能在您的示例)并且它在谨慎方面犯了错误。我看不到您的 case
表达式实际上可以输出 null
的任何方式,但根据我的经验,几乎所有计算列都将被视为可为空,但包裹在 isnull
中的列除外。
所以在你的测试用例中你可以替换
Create View ybTestNSB As
Select (CASE X WHEN 0 THEN Q1 ELSE CAST(0 as decimal(7,2)) END) AS Q From ybTest1
与
Create View ybTestNSB As
Select ISNULL(CASE X WHEN 0 THEN Q1 END, 0) AS Q From ybTest1
为了避免在此处放置令人讨厌的完全多余的表达式。