NOT NULL에서 NULL로 열 변경-어떤 일이 벌어지고 있습니까?


25

2.3B 개의 행이있는 테이블이 있습니다. 열을 NOT NULL에서 NULL로 변경하려고합니다. 열은 하나의 색인 (클러스터 또는 PK 색인이 아님)에 포함됩니다. 데이터 유형이 변경되지 않습니다 (INT입니다). 단지 무효 성입니다. 진술은 다음과 같습니다.

Alter Table dbo.Workflow Alter Column LineId Int NULL

작업을 중지하기 전에 작업이 10을 초과합니다 (차단 작업이므로 시간이 너무 오래 걸리기 때문에 아직 완료되지 않았습니다). 아마도 실제로 시간이 얼마나 걸리는지 테스트 해 테이블을 개발자 서버에 복사 할 것입니다. 그러나 NOT NULL에서 NULL로 변환 할 때 SQL Server가 수행중인 작업을 아는 사람이 있는지 궁금합니다. 또한 영향을받는 인덱스를 다시 작성해야합니까? 생성 된 쿼리 계획은 현재 상황을 나타내지 않습니다.

문제의 테이블이 클러스터되어 있습니다 (힙이 아님).


2
모든 리프 수준 데이터 페이지에서 null 비트 맵을 업데이트해야한다고 생각합니다. 그리고 2.3B 행을 사용하면 처리 할 페이지가 많이있을 것입니다. 나는 이것에 대해 너무 확신하지 못한다.
souplex

3
인덱스에 널 비트 맵을 배치하는 데 바쁠 수 있습니다. 인덱스 정의의 모든 열 부분이 NOT NULL로 정의 된 경우 NON-CLUSTERED INDEX에 NULL 비트 맵이 존재하지 않습니다.
souplex

답변:


27

주석에서 @Souplex에 의해 암시 된 바와 같이이 열이 NULL참여하는 비 클러스터형 인덱스 의 첫 번째 열일 경우 가능한 설명이있을 수 있습니다 .

다음 설정의 경우

CREATE TABLE Foo
  (
     A UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
     B CHAR(1) NOT NULL DEFAULT 'B'
  )

CREATE NONCLUSTERED INDEX ix
  ON Foo(B);

INSERT INTO Foo
            (B)
SELECT TOP 100000 'B'
FROM   master..spt_values v1,
       master..spt_values v2 

sys.dm_db_index_physical_stats는 비 클러스터형 인덱스 ix에 248 개의 리프 페이지와 단일 루트 페이지가 있음을 보여줍니다 .

인덱스 리프 페이지의 일반적인 행은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

그리고 루트 페이지에서

여기에 이미지 설명을 입력하십시오

그런 다음 실행 중 ...

CHECKPOINT;

GO

ALTER TABLE Foo ALTER COLUMN B  CHAR(1) NULL;


SELECT Operation, 
       Context,
       ROUND(SUM([Log Record Length]) / 1024.0,1) AS [Log KB],
       COUNT(*) as [OperationCount]
FROM sys.fn_dblog(NULL,NULL)
WHERE AllocUnitName = 'dbo.Foo.ix'
GROUP BY Operation, Context

반품

+-----------------+--------------------+-------------+----------------+
|    Operation    |      Context       |   Log KB    | OperationCount |
+-----------------+--------------------+-------------+----------------+
| LOP_SET_BITS    | LCX_GAM            | 4.200000    |             69 |
| LOP_FORMAT_PAGE | LCX_IAM            | 0.100000    |              1 |
| LOP_SET_BITS    | LCX_IAM            | 4.200000    |             69 |
| LOP_FORMAT_PAGE | LCX_INDEX_INTERIOR | 8.700000    |              3 |
| LOP_FORMAT_PAGE | LCX_INDEX_LEAF     | 2296.200000 |            285 |
| LOP_MODIFY_ROW  | LCX_PFS            | 16.300000   |            189 |
+-----------------+--------------------+-------------+----------------+

인덱스 리프를 다시 확인하면 행이 다음과 같이 보입니다.

여기에 이미지 설명을 입력하십시오

상단 페이지의 행은 아래와 같습니다.

여기에 이미지 설명을 입력하십시오

각 행이 업데이트되었으며 이제 NULL_BITMAP에 대한 다른 바이트와 함께 열 수에 대한 2 바이트가 포함됩니다.

여분의 행 너비로 인해 비 클러스터형 인덱스에는 이제 285 개의 리프 페이지와 루트 페이지와 함께 2 개의 중간 수준 페이지가 있습니다.

에 대한 실행 계획

 ALTER TABLE Foo ALTER COLUMN B  CHAR(1) NULL;

다음과 같이 보인다

여기에 이미지 설명을 입력하십시오

이렇게하면 기존 색인을 업데이트하고 페이지를 분할하지 않고 새로운 색인 사본을 작성합니다.


9

메타 데이터 만 업데이트하는 것이 아니라 비 클러스터형 인덱스를 확실히 다시 생성합니다. 이것은 SQL 2014에서 테스트되었으며 프로덕션 시스템에서는 실제로 테스트하지 않아야합니다.

CREATE TABLE [z](
    [a] [int] IDENTITY(1,1) NOT NULL,
    [b] [int] NOT NULL,
 CONSTRAINT [c_a] PRIMARY KEY CLUSTERED  ([a] ASC))
go
CREATE NONCLUSTERED INDEX [nc_b] on z (b asc)
GO
insert into z (b)
values (1);

그리고 이제 재미있는 부분은 :

DBCC IND (0, z, -1)

이렇게하면 테이블과 클러스터되지 않은 인덱스가 저장된 데이터베이스 페이지가 제공됩니다.

찾기 PagePID곳에 IndexID2이고, PageType2 다음 다음을 수행하십시오 :

DBCC TRACEON(3604) --are you sure that you are allowed to do this?

그리고:

dbcc page (0, 1, PagePID, 3) with tableresults

헤더에 널 비트 맵이 있습니다.

페이지 헤더 추출

이제하자 :

alter table z alter Column b int null;

정말로 참을성이 없다면 dbcc page명령을 다시 실행 해 볼 수는 있지만 실패 할 것이므로로 할당을 다시 확인하십시오 DBCC IND (0, z, -1). 마치 마법처럼 페이지가 이동했을 것입니다.

따라서 열의 Null 허용 여부를 변경하면 메타 데이터를 업데이트해야하고 나중에 인덱스를 다시 작성하지 않아도되므로 해당 열을 커버하는 비 클러스터형 인덱스의 저장소에 영향을줍니다.


SQL Server 2016부터 많은 ALTER TABLE ... ALTER COLUMN ...작업을 수행 할 수 있지만 다음을 수행하십시오 ONLINE.

ALTER TABLE (Transact-SQL)

  • 에서 열 변경 NOT NULL을하는 것은 NULL변경된 열이 클러스터되지 않은 인덱스에 의해 참조 될 때 온라인 작업으로 지원되지 않습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.