Warum wird eine berechnete Spalte NOT NULL in einer Ansicht als nullwertfähig betrachtet?

15

Ich habe einen Tisch:

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

Und eine Ansicht:

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

Ich habe ein DBML-Modell in C # (LinqToSQL) mit der FilteredRealty- Ansicht. Das [Ranking] -Feld wird als nullbares int erkannt und ich muss den Typ jedes Mal im generierten Code korrigieren, wenn ich etwas in der Datenbank ändere. Das ist sehr frustrierend für mich und viel Handarbeit.

In FilteredRealty werden keine Aggregate verwendet (in Bezug auf diese verwandte Frage ).

Warum wird die Spalte " Rangfolge" der Ansicht als nullwertfähig betrachtet, wenn " Realty.Ranking" nicht nullwertfähig ist?

Tomas Kubes
quelle

Antworten:

18

Das [Ranking]Feld wird als "Nullable" angezeigt, da es sich um eine berechnete Spalte handelt. Ja, es ist als deklariert NOT NULL, aber wie auf der MSDN-Seite für berechnete Spalten angegeben , kann das Datenbankmodul diese Bestimmung zur Abfragezeit ändern:

Das Datenbankmodul ermittelt anhand der verwendeten Ausdrücke automatisch, ob berechnete Spalten ungültig sind. Das Ergebnis der meisten Ausdrücke wird als nullwertfähig betrachtet, auch wenn nur nicht löschbare Spalten vorhanden sind, da mögliche Unter- oder Überläufe ebenfalls zu nullwerten Ergebnissen führen. Verwenden Sie die COLUMNPROPERTY-Funktion mit der AllowsNull- Eigenschaft, um die Nullwertfähigkeit einer berechneten Spalte in einer Tabelle zu untersuchen. Ein Ausdruck, der nullwertfähig ist, kann durch Angabe von ISNULL ( check_expression , constant ) in einen nicht nullwertfähigen Ausdruck umgewandelt werden , wobei die Konstante ein nicht nullwertiger Wert ist, der für ein beliebiges Nullergebnis eingesetzt wird.

Also mal sehen, ob das stimmt:

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  ==  :-(

Nun wollen wir sehen, ob ihre Ratschläge in Bezug auf ISNULLWerke:

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

Ihre Ratschläge scheinen korrekt zu sein. Versuchen wir also, dies auf die Definition der berechneten Spalte anzuwenden:

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

Und jetzt überprüfen wir die Eigenschaften noch einmal, aber für das neue Feld:

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

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

Dies sieht bisher positiv aus, aber selbst in der ursprünglichen Definition wurde von diesen beiden Überprüfungen "NOT NULL" gemeldet. Probieren wir also den echten Test aus - wie das Datenbankmodul die Nullfähigkeit zur Laufzeit ermittelt:

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

Um sicherzustellen, dass der berechnete Spaltenausdruck " Ranking" unter keinen Umständen NULL zurückgibt, müssen Sie ihn ISNULLmit einem geeigneten Standardwert einschließen. Beispielsweise:

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

Die NOT NULLEinschränkung stellt sicher, dass der dauerhafte Wert im Kontext der Einstellungen auf Tabellen- und Sitzungsebene, die beim Ändern der Tabelle wirksam werden, nicht null ist.

Wenn eine Abfrage jedoch auf diesen Ausdruck verweist, kann SQL Server zwischen der Verwendung des dauerhaften Werts (wenn die Einstellungen übereinstimmen) und der erneuten Berechnung des Ausdrucks wählen.

Einige Sitzungseinstellungen können beispielsweise dazu führen, dass ein Überlauf NULL zurückgibt, sodass SQL Server diese Möglichkeit berücksichtigen muss. Beim Zugriff über die Ansicht markiert SQL Server die Spalte korrekt als potenziell NULL zurückgebend.

Die Verwendung eines ISNULLAusdrucks in äußerster Randlage ist die einzige unterstützte Methode, um die gewünschten Ergebnisse zu erzielen. Verwenden COALESCEwird zum Beispiel nicht funktionieren.

Demo:

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

Beachten Sie die Verwendung von, sys.sp_refreshsqlmoduleda Ihre Ansicht kein Schema enthält.

Paul White Monica wieder einsetzen
quelle