如何帮助 SQL Server 识别我的索引视图列不能为 NULL?

Nic*_*mas 9 sql-server-2008 null constraint view

我在 SQL Server 2008 中定义了以下索引视图(您可以从 gist下载工作架构以进行测试):

CREATE VIEW dbo.balances
WITH SCHEMABINDING
AS
SELECT
      user_id
    , currency_id

    , SUM(transaction_amount)   AS balance_amount
    , COUNT_BIG(*)              AS transaction_count
FROM dbo.transactions
GROUP BY
      user_id
    , currency_id
;
GO

CREATE UNIQUE CLUSTERED INDEX UQ_balances_user_id_currency_id
ON dbo.balances (
      user_id
    , currency_id
);
GO
Run Code Online (Sandbox Code Playgroud)

user_idcurrency_idtransaction_amount都定义为 中的NOT NULLdbo.transactions。但是,当我查看 Management Studio 的对象资源管理器中的视图定义时,它在视图中将balance_amount和都标记transaction_countNULL可列。

我查看了几个讨论,是其中最相关的一个,建议对函数进行一些改组可能有助于 SQL Server 识别视图列始终为NOT NULL. 但是,在我的情况下不可能进行这种改组,因为在索引视图中不允许使用聚合函数上的表达式(例如ISNULL()在 上SUM())。

  1. 有什么方法可以帮助 SQL Server 识别balance_amount并且transaction_count可以NOT NULL吗?

  2. 如果没有,我是否应该担心这些列被错误地识别为NULL-able?

    我能想到的两个问题是:

    • 映射到余额视图的任何应用程序对象都获得了错误的余额定义。
    • 在非常有限的情况下,某些优化对查询优化器不可用,因为它不能保证这两列是NOT NULL.

    这些问题中的任何一个都是大问题吗?我还有其他需要注意的问题吗?

Jac*_*las 10

user_idcurrency_id、 和transaction_amount都被定义为NOT NULLdbo.transactions

在我看来,SQL Server 有一个笼统的假设,即null即使它操作的字段是not null. 在某些情况下这显然是正确的:

create table foo(bar integer not null);
select sum(bar) from foo
-- returns 1 row with `null` field
Run Code Online (Sandbox Code Playgroud)

并且在group bylike的广义版本中也是如此cube

这个更简单的测试用例说明了任何聚合都被解释为可以为空的观点:

CREATE VIEW dbo.balances
with schemabinding
AS
SELECT
      user_id
    , sum(1)   AS balance_amount
FROM dbo.transactions
GROUP BY
      user_id
;
GO
Run Code Online (Sandbox Code Playgroud)

IMO 这是 SQL Server 的一个限制(尽管是次要的)-其他一些 RDBMS 允许对未强制执行的视图创建某些约束,并且存在仅用于向优化器提供线索,尽管我认为“唯一性”更有可能有助于生成比“可空性”更好的查询计划


如果列的可空性很重要,也许是为了与 ORM 一起使用,请考虑将索引视图包装在另一个视图中,该视图使用以下方法简单地保证非可空性ISNULL

CREATE VIEW dbo.balancesORM
WITH SCHEMABINDING
AS
SELECT 
    B.[user_id],
    B.currency_id,
    balance_amount = ISNULL(B.balance_amount, 0),
    transaction_count = ISNULL(B.transaction_count, 0)
FROM dbo.balances AS B;
Run Code Online (Sandbox Code Playgroud)

SSMS 对象资源管理器详细信息


Aar*_*and 5

我认为没有任何方法可以强制 SQL Server 将这些列识别为不可为空,即使它们显然不是。例如,您可以尝试更改在内部表达式中定义ISNULL/的顺序,但这无济于事。COALESCE SUM()

我也不相信您会错过任何优化 - 这些列当前未编入索引,因此优化器不能选择不同的访问方法来确定所有balance_amount值 > 10000。有可能是这样一种情况,如果您在这些列之一上创建非聚集索引,您可能会得到比没有索引时更好的估计值,但这与可空性无关。

从性能的角度来看,我不会太担心这一点。我回去查看了我多年来创建的一堆索引视图,这些聚合列都是可以为空的。他们表现得很好。

再一次,就对象映射而言,我不会太担心。由于应用程序无法更新索引视图,因此它认为balance_amount可以是null. 它永远不会收到 a null,也不能尝试写 a null,所以<shrug>.