뷰에서 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

FilteredRealty 보기가 있는 C # (LinqToSQL)의 dbml 모델 이 있습니다. [순위] 필드는 null 허용 int로서 나는 내가 데이터베이스에서 아무것도 변경할 때 생성 된 코드의 모든 시간을 종류를 해결하기 위해 그래서 인식되고있다. 이것은 저에게 매우 실망스럽고 많은 수동 작업입니다.

FilteredRealty에 사용 된 집계가 없습니다 ( 이 관련 질문과 관련 하여 ).

Realty.Ranking 이 널 입력 가능하지 않은 경우보기 의 순위 열이 널 입력 가능으로 간주되는 이유는 무엇 입니까?

답변:


19

[Ranking]필드 인해 계산 열 것에 "null 허용"으로 표시된다. 예,로 선언되어 NOT NULL있지만 계산 열 상태 의 MSDN 페이지 에서 데이터베이스 엔진은 쿼리시 해당 결정을 변경할 수 있습니다.

데이터베이스 엔진은 사용 된 식을 기반으로 계산 된 열의 Null 허용 여부를 자동으로 결정합니다. Null을 허용하지 않는 열만 존재하더라도 대부분의 식의 결과는 Null을 허용하는 것으로 간주됩니다. 가능한 언더 플로 또는 오버플로로 인해 Null 결과도 생성되기 때문입니다. 테이블에서 계산 열의 Null 허용 여부를 조사 하려면 AllowsNull 속성 과 함께 COLUMNPROPERTY 함수를 사용하십시오 . ISNULL ( check_expression , constant ) 을 지정하여 널 입력 가능 표현식을 널 입력 불가능 표현식으로 변환 할 수 있습니다 . 여기서 상수 는 널 (NULL) 결과로 대체되는 널이 아닌 값입니다.

이것이 사실인지 봅시다 :

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

이것은 지금까지 긍정적으로 보이지만 원래 정의조차도이 두 가지 검사에서 "NOT NULL"을보고했습니다. 데이터베이스 엔진이 런타임시 null을 결정하는 방법에 대한 실제 테스트를 해보자.

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

13

보장하기 위해 순위 계산 열 표현이 어떤 상황에서 NULL을 반환하지 않습니다, 당신은 그것을 포장해야한다 ISNULL적절한 디폴트 값. 예를 들면 다음과 같습니다.

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

NOT NULL제약 조건은 테이블이 수정 될 때 지속 된 값이 적용되는 테이블 - 및 세션 수준 설정의 맥락에서, null가 아닌 보장합니다.

그러나 쿼리가 해당 식을 참조하면 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뷰가 스키마 바운드가 아니기 때문에 사용에 유의하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.