Pourquoi une colonne calculée NOT NULL est-elle considérée comme nullable dans une vue?


15

J'ai une table:

CREATE TABLE [dbo].[Realty](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [RankingBonus] [int] NOT NULL,
    [Ranking]  AS ([Id]+[RankingBonus]) PERSISTED NOT NULL
    ....
)

Et une vue:

CREATE View  [dbo].[FilteredRealty] AS
 SELECT 
realty.Id as realtyId,
...
COALESCE(realty.Wgs84X, ruian_cobce.Wgs84X, ruian_obec.Wgs84X) as Wgs84X,
COALESCE(realty.Wgs84Y, ruian_cobce.Wgs84Y, ruian_obec.Wgs84Y) as Wgs84Y,
realty.Ranking,
...
FROM realty
JOIN Category ON realty.CategoryId = Category.Id
LEFT JOIN ruian_cobce ON realty.cobceId = ruian_cobce.cobce_kod
LEFT JOIN ruian_obec ON realty.obecId = ruian_obec.obec_kod
LEFT JOIN okres ON realty.okresId = okres.okres_kod
LEFT JOIN ExternFile ON realty.Id = ExternFile.ForeignId AND ExternFile.IsMain = 1
                     AND ExternFile.ForeignTable = 5
INNER JOIN Person ON realty.OwnerId = Person.Id
WHERE Person.ConfirmStatus = 1

J'ai un modèle dbml en C # (LinqToSQL) avec la vue FilteredRealty dedans. Le champ [Ranking] est reconnu comme un entier nullable et je dois donc corriger le type dans le code généré à chaque fois que je change quoi que ce soit dans la base de données. C'est très frustrant pour moi et beaucoup de travail manuel.

Aucun agrégat n'est utilisé dans FilteredRealty (concernant cette question connexe ).

Pourquoi la colonne Classement de la vue est-elle considérée comme annulable si Realty.Ranking est non annulable?

Réponses:


19

Le [Ranking]champ s'affiche comme "Nullable" car il s'agit d'une colonne calculée. Oui, il est déclaré comme NOT NULL, mais comme l' indique la page MSDN pour les colonnes calculées , le moteur de base de données peut modifier cette détermination au moment de la requête:

Le moteur de base de données détermine automatiquement la nullité des colonnes calculées en fonction des expressions utilisées. Le résultat de la plupart des expressions est considéré comme nullable même si seules des colonnes non nullables sont présentes, car les débordements ou débordements possibles produiront également des résultats nuls. Utilisez la fonction COLUMNPROPERTY avec la propriété AllowsNull pour rechercher la nullité de toute colonne calculée dans une table. Une expression qui peut être nullable peut être transformée en une expression non nullable en spécifiant ISNULL ( expression_contrôle , constante ), où la constante est une valeur non nulle substituée à tout résultat nul.

Voyons donc si cela est vrai:

CREATE TABLE [dbo].[Realty](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [RankingBonus] [int] NOT NULL,
    [Ranking]  AS ([Id]+[RankingBonus]) PERSISTED NOT NULL
);
GO

EXEC sp_help 'dbo.Realty';
-- Ranking: Nullable = "no"

SELECT COLUMNPROPERTY(OBJECT_ID(N'dbo.Realty'), N'Ranking', 'AllowsNull') AS [AllowsNull?];
-- 0

SELECT * FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM dbo.Realty', '', NULL);
-- Ranking: is_nullable = 1  ==  :-(

Voyons maintenant si leurs conseils concernant les ISNULLœuvres:

SELECT * FROM sys.dm_exec_describe_first_result_set(
   N'SELECT Id, RankingBonus, ISNULL(Ranking, -99) AS [RealRanking] FROM dbo.Realty;',
   '',
   NULL);
-- RealRanking: is_nullable = 0

Leurs conseils semblent exacts, essayons donc de les appliquer à la définition de la colonne calculée:

ALTER TABLE dbo.Realty
  ADD [RankingFixed] AS (ISNULL(([Id]+[RankingBonus]), -99))
  PERSISTED NOT NULL;
GO

Et maintenant, nous vérifions à nouveau les propriétés, mais pour le nouveau champ:

EXEC sp_help 'dbo.Realty';
-- RankingFixed: Nullable = "no"

SELECT COLUMNPROPERTY(OBJECT_ID(N'dbo.Realty'),
                      N'RankingFixed',
                      'AllowsNull') AS [AllowsNullsNow?];
-- 0

Cela semble positif jusqu'à présent, mais même la définition d'origine indiquait "NON NUL" à partir de ces deux vérifications. Essayons donc le vrai test - comment le moteur de base de données détermine la nullité au moment de l'exécution:

SELECT * FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM dbo.Realty', '', NULL);
-- RankingFixed: is_nullable = 0  ==  :-) WOO HOO!

13

Pour garantir que l' expression de colonne calculée Classement ne renvoie en aucun cas NULL, vous devez l'encapsuler ISNULLavec une valeur par défaut appropriée. Par exemple:

Ranking AS ISNULL(Id + RankingBonus, 0) PERSISTED NOT NULL

La NOT NULLcontrainte garantit que la valeur persistante n'est pas nulle, dans le contexte des paramètres de niveau table et session en vigueur lorsque la table est modifiée.

Toutefois, lorsqu'une requête fait référence à cette expression, SQL Server a le choix entre utiliser la valeur persistante (si les paramètres correspondent) ou calculer à nouveau l'expression.

Certains paramètres de session peuvent provoquer un débordement pour retourner NULL, par exemple, donc SQL Server doit tenir compte de cette possibilité. Lorsqu'il est accessible via la vue, SQL Server marque correctement la colonne comme renvoyant potentiellement un NULL.

L'utilisation d'une ISNULLexpression la plus externe sur l'expression est le seul moyen pris en charge pour obtenir ce que vous voulez. L'utilisation COALESCEne fonctionnera pas, par exemple.

Démo:

CREATE TABLE dbo.T1
(
    c1 integer NOT NULL,
    c2 integer NOT NULL,
    c3 AS c1 + c2 PERSISTED NOT NULL
);
GO
CREATE VIEW dbo.V1
AS
SELECT T.c1,
       T.c2,
       T.c3
FROM dbo.T1 AS T;
GO
SELECT AllowsNull = COLUMNPROPERTY(OBJECT_ID(N'dbo.V1', N'V'), N'c3', 'AllowsNull');
GO
ALTER TABLE dbo.T1
DROP COLUMN c3;
GO
ALTER TABLE dbo.T1
ADD c3 AS ISNULL(c1 + c2, 0) PERSISTED NOT NULL;
GO
EXECUTE sys.sp_refreshsqlmodule
    @name = N'dbo.V1';
GO
SELECT AllowsNull = COLUMNPROPERTY(OBJECT_ID(N'dbo.V1', N'V'), N'c3', 'AllowsNull');
GO
DROP VIEW dbo.V1;
DROP TABLE dbo.T1;
GO

Notez l'utilisation de sys.sp_refreshsqlmodulecar votre vue n'est pas schématisée.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.