Чому обчислюваний стовпець NOT NULL вважається нульовим для перегляду?


15

У мене є таблиця:

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

І вид:

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

У мене є модель dbml в C # (LinqToSQL) з поданням FilteredRealty . Поле [Рейтинг] визнається нульовим int, тому мені доведеться виправляти тип у створеному коді кожного разу, коли я щось міняю в базі даних. Це дуже засмучує мене і багато ручної роботи.

Немає агрегатів, які використовуються у FilteredRealty (стосовно цього пов'язаного питання ).

Чому стовпець " Рейтинг " у представленні вважається нульовим, якщо Realty.Ranking не є нульовим ?

Відповіді:


19

[Ranking]Поле показує , як «Nullable» з - за того , щоб бути обчислюваний стовпець. Так, це оголошено як NOT NULL, але, як стверджується на сторінці MSDN для обчислених стовпців , двигун бази даних може змінити це визначення в момент запиту:

Механізм баз даних автоматично визначає нульовість обчислених стовпців на основі використаних виразів. Результат більшості виразів вважається нульовим, навіть якщо присутні лише стовпчики, що не знімаються, оскільки можливі підтоки або переливи також дадуть нульові результати. Використовуйте функцію COLUMNPROPERTY з властивістю AllowNull, щоб дослідити зведеність на нічиїх обчислених стовпців у таблиці. Вираз, який є нульовим, можна перетворити на незмінне, вказавши ISNULL ( check_expression , константа ), де константа є ненульовим значенням, заміщеним будь-яким нульовим результатом.

Отже, давайте подивимось, чи це правда:

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

Тепер давайте подивимось, чи є їхні поради щодо ISNULLробіт:

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

Їх поради здаються точними, тому спробуємо застосувати це до визначення обчисленого стовпця:

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

А тепер ми ще раз перевіряємо властивості, але для нового поля:

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

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

Поки що це виглядає позитивно, але навіть початкове визначення повідомляло "НЕ НУЛЬНИЙ" з цих двох перевірок. Тож давайте спробуємо справжній тест - як двигун бази даних визначає нульовість під час виконання:

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

13

Щоб гарантувати, що виражений обчислюваний стовпець Ranking не повертає NULL за жодних обставин, потрібно обгорнути його ISNULLвідповідним значенням за замовчуванням. Наприклад:

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

NOT NULLОбмеження гарантує , збережене значення не дорівнює нулю, в контексті table- і сеансового рівня настройки в дію , коли таблиця змінюється.

Однак, коли запит посилається на цей вираз, SQL Server має вибір між використанням збереженого значення (якщо параметри відповідають) або обчисленням виразу заново.

Деякі параметри сеансу можуть призвести до того, що переповнення поверне NULL, наприклад, SQL Server повинен враховувати цю можливість. При доступі через представлення даних, SQL Server правильно позначає стовпець як потенційно повертає NULL.

Використання самого зовнішнього ISNULLвиразу - це єдиний підтримуваний спосіб досягти того, що ви хочете. COALESCEНаприклад, використання не буде працювати.

Демонстрація:

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

Зверніть увагу на використання, sys.sp_refreshsqlmoduleоскільки ваш погляд не пов'язаний зі схемою.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.