Como posso ajudar o SQL Server a reconhecer que minha coluna de exibição indexada NÃO é capaz de NULL?

9

Eu tenho a seguinte exibição indexada definida no SQL Server 2008 (você pode baixar um esquema funcional da gist para fins de teste):

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_ide transaction_amountsão todos definidos como NOT NULLcolunas em dbo.transactions. No entanto, quando olho para a definição de exibição no Pesquisador de Objetos do Management Studio, ela marca as colunas como balance_amounte -able na exibição.transaction_countNULL

Examinei várias discussões, sendo esta a mais relevante, que sugerem que algumas funções aleatórias podem ajudar o SQL Server a reconhecer que uma coluna de exibição é sempre NOT NULL. No entanto, não é possível embaralhar esse tipo, pois expressões em funções agregadas (por exemplo, um ISNULL()over the SUM()) não são permitidas em visualizações indexadas.

  1. Existe alguma maneira eu posso ajudar SQL Server reconhecer que balance_amounte transaction_countsão NOT NULL-able?

  2. Caso contrário, devo ter alguma preocupação sobre o fato de essas colunas serem identificadas por engano como NULL-able?

    As duas preocupações em que consigo pensar são:

    • Quaisquer objetos de aplicativo mapeados para a exibição de saldos estão recebendo uma definição incorreta de um saldo.
    • Em casos muito limitados, certas otimizações não estão disponíveis para o Query Optimizer, pois não há garantia da exibição de que essas duas colunas estão NOT NULL.

    Alguma dessas preocupações é um grande negócio? Há outras preocupações que devo ter em mente?

Nick Chammas
fonte
Sim, existem preocupações, por exemplo, seu ORM criará tipos anuláveis, que, por sua vez, precisarão de um cuidado extra no código ao usá-los, o que é inútil (ou até enganoso) no seu caso.
Marcel
Isso também parece ser um problema em um cte recursivo ao repetir em um campo não anulável (sem agregação), embora um IsNull (..., 0) no final possa curar.
crokusek

Respostas:

10

user_id,, currency_ide transaction_amountsão todos definidos como NOT NULLcolunas emdbo.transactions

Parece-me que o SQL Server tem uma suposição geral de que um agregado pode produzir um nullmesmo que os campos nos quais ele opera sejam not null. Isto é obviamente verdade em certos casos:

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

E também é verdade nas versões generalizadas de group bylikecube

Este caso de teste mais simples ilustra o ponto em que qualquer agregado é interpretado como sendo anulável:

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

Na IMO, essa é uma limitação (embora menor) do SQL Server - alguns outros RDBMSs permitem a criação de certas restrições nas exibições que não são impostas e existem apenas para fornecer pistas para o otimizador, embora eu ache que a 'exclusividade' é mais provável. ajudar a gerar um bom plano de consulta além da 'nulidade'


Se a nulidade da coluna for importante, talvez para uso com um ORM, considere agrupar a exibição indexada em outra exibição que simplesmente garanta a não nulidade usando 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;

Detalhes do SSMS Object Explorer

Jack diz que tenta topanswers.xyz
fonte
5

Acho que não há como você forçar o SQL Server a reconhecer essas colunas como não anuláveis, mesmo que claramente não. Você pode tentar alterar a ordem de como define ISNULL/ em COALESCEtorno da expressão interna SUM() , por exemplo, mas isso não vai ajudar.

Também não acredito que haja otimizações que você perderá - essas colunas não estão indexadas no momento, por isso não é como se o otimizador pudesse escolher um método de acesso diferente para determinar, digamos, todos os balance_amountvalores> 10000. pode ser uma situação em que, se você criar um índice não agrupado em uma dessas colunas, poderá obter estimativas um pouco melhores do que se o índice não estiver lá, mas isso não tem nada a ver com a nulidade.

Eu não ficaria muito preocupado com isso do ponto de vista de desempenho. Voltei e observei várias visualizações indexadas que criei ao longo dos anos e essas colunas de agregação são todas anuláveis. Eles executam muito bem.

Quanto ao mapeamento de objetos, novamente, eu não estaria muito preocupado com isso. Como o aplicativo não pode atualizar a exibição indexada, não importa se acha que balance_amountpode ser null. Ele nunca receberá um nulle não pode tentar escrever um null, então <shrug>.

Aaron Bertrand
fonte
@ Aaron, sobre mapeamento de objetos: considero que vale a pena examinar, pois um mapeador provavelmente gerará objetos inúteis / enganosos com tipos anuláveis ​​que nunca serão realmente usados ​​como tais.
Marcel