Dlaczego kolumna obliczeniowa NOT NULL jest uważana za zerową w widoku?

15

Mam stolik:

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

I widok:

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

Mam model dbml w C # (LinqToSQL) z widokiem FilteredRealty . Pole [Ranking] jest rozpoznawane jako wartość zerowa int, dlatego muszę poprawiać typ w generowanym kodzie za każdym razem, gdy zmieniam cokolwiek w bazie danych. Jest to dla mnie bardzo frustrujące i wymaga dużo pracy fizycznej.

W FilteredRealty nie ma żadnych agregatów (dotyczących tego powiązanego pytania ).

Dlaczego kolumna Ranking widoku jest uważana za zerową, jeśli Realty.Ranking nie ma wartości zerowej ?

Tomas Kubes
źródło

Odpowiedzi:

19

[Ranking]Pole pokazuje jako „Nullable” ze względu na to kolumna komputerowej. Tak, jest zadeklarowany jako NOT NULL, ale jako strona MSDN dla stanów Kolumny obliczane, aparat bazy danych może zmienić to określenie w czasie zapytania:

Aparat baz danych automatycznie określa zerowalność obliczanych kolumn na podstawie użytych wyrażeń. Wynik większości wyrażeń jest uważany za zerowalny, nawet jeśli obecne są tylko kolumny, które nie mają wartości zerowych, ponieważ możliwe niedopełnienia lub przepełnienia spowodują również wyzerowanie wyników. Użyj funkcji COLUMNPROPERTY z właściwością AllowsNull, aby zbadać zerowalność dowolnej kolumny obliczeniowej w tabeli. Wyrażenie, które jest zerowalne, można przekształcić w wyrażenie niewymienne, określając ISNULL ( wyrażenie_wyrażające , stała ), gdzie stała jest wartością inną niż zerowa podstawioną na dowolny wynik zerowy.

Zobaczmy, czy to prawda:

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

Zobaczmy teraz, czy ich porady dotyczące ISNULLprac:

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

Ich porady wydają się trafne, spróbujmy więc zastosować to do definicji kolumny obliczeniowej:

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

A teraz ponownie sprawdzamy właściwości, ale dla nowego pola:

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

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

Jak dotąd wygląda to pozytywnie, ale nawet pierwotna definicja podała „NOT NULL” z tych dwóch kontroli. Spróbujmy więc prawdziwego testu - w jaki sposób silnik bazy danych określa zerowalność w czasie wykonywania:

SELECT * FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM dbo.Realty', '', NULL);
-- RankingFixed: is_nullable = 0  ==  :-) WOO HOO!
Solomon Rutzky
źródło
13

Aby zagwarantować, że wyliczone wyrażenie rankingowe w żadnym wypadku nie zwróci wartości NULL, należy zawinąć je w ISNULLodpowiednią wartość domyślną. Na przykład:

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

NOT NULLOgraniczenie zapewnia wartość Trwały nie jest null, w kontekście ustawień Tabeli-a na poziomie sesji w efekcie gdy stół jest modyfikowany.

Jednak, gdy zapytanie odwołuje się do tego wyrażenia, SQL Server ma wybór pomiędzy użyciem utrwalonej wartości (jeśli ustawienia są zgodne) lub obliczeniem wyrażenia na nowo.

Niektóre ustawienia sesji mogą powodować, że przepełnienie zwróci NULL, na przykład SQL Server musi uwzględnić tę możliwość. Po uzyskaniu dostępu przez widok SQL Server poprawnie oznacza kolumnę jako potencjalnie zwracającą wartość NULL.

Używanie skrajnego ISNULLwyrażenia jest jedynym obsługiwanym sposobem na osiągnięcie tego, co chcesz. Na przykład korzystanie COALESCEnie będzie działać.

Próbny:

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

Zwróć uwagę na użycie, sys.sp_refreshsqlmoduleponieważ twój widok nie jest schematyczny.

Paul White 9
źródło