Comment puis-je aider SQL Server à reconnaître que ma colonne de vue indexée n'est PAS compatible avec NULL?

9

J'ai la vue indexée suivante définie dans SQL Server 2008 (vous pouvez télécharger un schéma de travail à partir de gist à des fins de test):

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_idet transaction_amountsont tous définis comme des NOT NULLcolonnes dans dbo.transactions. Cependant, lorsque je regarde la définition de la vue dans l'Explorateur d'objets de Management Studio, elle marque les deux colonnes balance_amountet en transaction_counttant que NULL-able dans la vue.

J'ai jeté un coup d'œil à plusieurs discussions, celle-ci étant la plus pertinente d'entre elles, qui suggèrent qu'un mélange des fonctions peut aider SQL Server à reconnaître qu'une colonne de vue est toujours NOT NULL. Un tel mélange n'est pas possible dans mon cas, cependant, car les expressions sur les fonctions d'agrégation (par exemple un ISNULL()sur le SUM()) ne sont pas autorisées dans les vues indexées.

  1. Est - il possible que je peux aider SQL Server reconnaître que balance_amountet transaction_countsont NOT NULLable?

  2. Sinon, devrais-je craindre que ces colonnes soient identifiées par erreur comme étant NULL-able?

    Les deux préoccupations auxquelles je pouvais penser sont:

    • Tous les objets d'application mappés à la vue des soldes obtiennent une définition incorrecte d'un solde.
    • Dans des cas très limités, certaines optimisations ne sont pas disponibles pour l'Optimiseur de requête car il n'a pas de garantie de la vue que ces deux colonnes le sont NOT NULL.

    L'une ou l'autre de ces préoccupations est-elle un gros problème? Y a-t-il d'autres préoccupations que je dois garder à l'esprit?

Nick Chammas
la source
Oui, il y a des problèmes, par exemple, votre ORM créera des types nullables, qui à leur tour auront besoin de précautions supplémentaires dans le code lors de leur utilisation, ce qui est inutile (ou même trompeur) dans votre cas.
Marcel
Cela semble également être un problème dans un cte récursif lors de la récursivité sur un champ non nullable (pas d'agrégat) bien qu'un IsNull (..., 0) à la fin puisse guérir.
crokusek

Réponses:

10

user_id,, currency_idet transaction_amountsont tous définis comme des NOT NULLcolonnes dansdbo.transactions

Il me semble que SQL Server a une hypothèse générale qu'un agrégat peut produire un nullmême si le ou les champs sur lesquels il opère le sont not null. C'est évidemment vrai dans certains cas:

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

Et est également vrai dans les versions généralisées de group bycommecube

Ce cas de test plus simple illustre le point que tout agrégat est interprété comme pouvant être annulé:

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

OMI, il s'agit d'une limitation (quoique mineure) de SQL Server - certains autres SGBDR permettent la création de certaines contraintes sur les vues qui ne sont pas appliquées et n'existent que pour donner des indices à l'optimiseur, bien que je pense que "l'unicité" est plus susceptible de aider à générer un bon plan de requête que la «nullité»


Si la valeur NULL de la colonne est importante, peut - être pour une utilisation avec un ORM, pensez à envelopper la vue indexée dans une autre vue que le simple garantit l'utilisation de non-valeur NULL 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;

Détails de l'explorateur d'objets SSMS

Jack dit d'essayer topanswers.xyz
la source
5

Je ne pense pas qu'il soit possible de forcer SQL Server à reconnaître ces colonnes comme non nulles, même si elles ne le sont clairement pas. Vous pouvez essayer de changer l'ordre dans lequel vous définissez ISNULL/ COALESCEautour de l'expression à l' intérieur SUM() , par exemple, mais cela ne va pas aider.

Je ne crois pas non plus qu'il y ait des optimisations que vous allez manquer - ces colonnes ne sont pas actuellement indexées, donc ce n'est pas comme si l'optimiseur peut choisir une méthode d'accès différente pour déterminer, disons, toutes les balance_amountvaleurs> 10000. Là peut être une situation où si vous créez un index non clusterisé sur l'une de ces colonnes, vous pourriez obtenir des estimations légèrement meilleures que si l'index n'est pas là, mais cela n'a rien à voir avec la nullité.

Je ne serais pas trop inquiet à ce sujet du point de vue des performances. Je suis retourné en arrière et j'ai regardé un tas de vues indexées que j'ai créées au fil des ans et ces colonnes d'agrégation sont toutes annulables. Ils fonctionnent très bien.

En ce qui concerne le mappage d'objets, encore une fois, je ne serais pas trop inquiet à ce sujet. Étant donné que l'application ne peut pas mettre à jour la vue indexée, peu importe si elle pense que balance_amountpeut être null. Il ne va jamais recevoir null, et il ne peut pas essayer d'écrire un null, donc <shrug>.

Aaron Bertrand
la source
@Aaron, à propos du mappage d'objets: je considère qu'il vaut la peine de regarder, car un mappeur générera probablement des objets inutiles / trompeurs avec des types nullables qui ne seront jamais vraiment utilisés en tant que tels.
Marcel