Как я могу помочь SQL Server распознать, что мой индексированный столбец НЕ поддерживает NULL?

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_id, currency_idи transaction_amountвсе определены как NOT NULLстолбцы в dbo.transactions. Однако когда я смотрю на определение представления в обозревателе объектов Management Studio, оно помечает столбцы как balance_amountи transaction_countкак NULLдоступные в представлении.

Я принял взглянуть на несколько дискуссий, это один является наиболее важным из них, которые предполагают некоторую перестановку функций может помочь SQL Server признать , что столбец вид всегда NOT NULL. Однако в моем случае такая перетасовка невозможна, так как выражения в агрегатных функциях (например, ISNULL()над over SUM()) не допускаются в индексированных представлениях.

  1. Можно ли как-то помочь SQL Server распознать это balance_amountи transaction_countможно NOT NULLли?

  2. Если нет, следует ли мне NULLопасаться, что эти столбцы ошибочно определены как -able?

    Две проблемы, о которых я мог подумать:

    • Любые объекты приложения, сопоставленные с представлением сальдо, получают неверное определение сальдо.
    • В очень ограниченных случаях определенные оптимизации не доступны Оптимизатору запросов, так как он не имеет гарантии, исходя из того, что эти два столбца доступны NOT NULL.

    Является ли что-то из этого серьезным? Есть ли еще какие-то проблемы, которые я должен иметь в виду?

Ник Чаммас
источник
Да, существуют проблемы, например, ваш ORM будет создавать типы, допускающие обнуление, которые, в свою очередь, потребуют дополнительной осторожности в коде при их использовании, что бесполезно (или даже вводит в заблуждение) в вашем случае.
Марсель
Это также кажется проблемой в рекурсивном cte при повторном обращении к ненулевому полю (без агрегата), хотя IsNull (..., 0) в конце может вылечить.
Crokusek

Ответы:

10

user_id, currency_idи transaction_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 byкак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 - некоторые другие СУБД позволяют создавать определенные ограничения для представлений, которые не применяются и существуют только для того, чтобы дать подсказки оптимизатору, хотя я думаю, что «уникальность» более вероятна помочь в создании хорошего плана запроса, чем «обнуляемость»


Если обнуляемость столбца важна, возможно, для использования с 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

Джек говорит, попробуйте topanswers.xyz
источник
5

Я не думаю, что есть какой-то способ заставить SQL Server распознавать эти столбцы как необнуляемые, даже если они явно нет. Например, вы можете попытаться изменить порядок определения ISNULL/ COALESCEвокруг выражения внутри SUM() , но это не поможет.

Я также не верю, что есть какие-то оптимизации, которые вы пропустите - эти столбцы в настоящее время не индексируются, поэтому не похоже, чтобы оптимизатор мог выбрать другой метод доступа для определения, скажем, всех balance_amountзначений> 10000. может возникнуть ситуация, когда, если вы создадите некластеризованный индекс для одного из этих столбцов, вы можете получить более точные оценки, чем если бы индекс отсутствовал, но это не имеет ничего общего с обнуляемостью.

Я не был бы слишком обеспокоен этим с точки зрения производительности. Я вернулся и посмотрел на несколько индексированных представлений, созданных мной за эти годы, и все эти столбцы агрегирования обнуляются. Они работают просто отлично.

Что касается сопоставления объектов, я бы не стал слишком беспокоиться об этом. Поскольку приложение не может обновить индексированное представление, не имеет значения, если оно считает, что это balance_amountможет быть null. Он никогда не получит null, и не может попытаться написать null, так <shrug>.

Аарон Бертран
источник
@Aaron, об объектном отображении: я считаю, что стоит посмотреть, так как картограф, скорее всего, будет генерировать бесполезные / вводящие в заблуждение объекты с обнуляемыми типами, которые никогда не будут использоваться как таковые.
Марсель