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


9

我在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

user_idcurrency_idtransaction_amount都定义为中的NOT NULLdbo.transactions。但是,当我看着Management Studio中的对象资源管理器,它标志着这两个视图定义balance_amount,并transaction_countNULL在视图-able列。

我看了几个讨论,其中最相关的讨论表明一些功能改组可以帮助SQL Server识别视图列始终为NOT NULL。不过,在我的情况下,这样的改组是不可能的,因为在索引视图中不允许使用聚合函数的表达式(例如ISNULL()over SUM())。

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

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

    我能想到的两个问题是:

    • 映射到余额视图的所有应用程序对象都获得了错误的余额定义。
    • 在非常有限的情况下,某些优化不适用于Query Optimizer,因为从视图的角度来看无法保证这两个列都是NOT NULL

    这些担忧中有什么大不了的吗?我还有其他需要注意的问题吗?


是的,有一些顾虑,例如您的ORM将创建可为空的类型,这反过来将在使用它们时在代码中需要格外小心,这在您的情况下是无用的(甚至会引起误解)。
马塞尔(Marcel)

尽管最后一个IsNull(...,0)可以解决,但在不可空字段(没有聚合)上进行递归时,这似乎也是递归cte中的问题。
crokusek 2014年

Answers:


10

user_idcurrency_idtransaction_amount都被定义为NOT NULL在列dbo.transactions

在我看来,SQL Server有一个笼统的假设,即null即使聚合操作的字段为,聚合也可以产生一个偶数not null。在某些情况下显然是这样:

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

并且在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

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;

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


5

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

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

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

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



@Aaron,关于对象映射:我认为值得一看,因为映射器可能会生成具有可空类型的无用/误导性对象,这些类型将永远不会真正使用。
马塞尔(Marcel)2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.