Thay đổi một cột từ KHÔNG NULL thành NULL - Điều gì đang diễn ra dưới mui xe?


25

Chúng tôi có một bảng với các hàng 2,3B trong đó. Chúng tôi muốn thay đổi một cột từ KHÔNG NULL thành NULL. Cột được chứa trong một chỉ mục (không phải chỉ mục cụm hoặc PK). Kiểu dữ liệu không thay đổi (đó là INT). Chỉ là sự vô hiệu. Tuyên bố như sau:

Alter Table dbo.Workflow Alter Column LineId Int NULL

Hoạt động mất hơn 10 trước khi chúng tôi dừng hoạt động (chúng tôi thậm chí chưa cho phép nó chạy đến khi hoàn thành vì đó là một hoạt động chặn và mất quá nhiều thời gian). Có lẽ chúng ta sẽ sao chép bảng vào một máy chủ dev để kiểm tra xem nó thực sự mất bao lâu. Nhưng, tôi tò mò liệu có ai biết SQL Server đang làm gì khi chuyển đổi từ KHÔNG NULL sang NULL không? Ngoài ra, các chỉ số bị ảnh hưởng sẽ cần phải được xây dựng lại? Kế hoạch truy vấn được tạo không chỉ ra điều gì đang xảy ra.

Bảng trong câu hỏi được nhóm lại (không phải là một đống).


2
Tôi nghĩ rằng nó sẽ phải cập nhật bitmap null trên tất cả các dữ liệu cấp độ lá. Và với các hàng 2.3B, tôi cá là nó sẽ có rất nhiều trang để xử lý. Tôi không quá chắc chắn về điều này mặc dù.
souplex

3
Có thể bận rộn đặt một bitmap null trên chỉ mục quá. Bitmap NULL sẽ KHÔNG xuất hiện trong INDEX KHÔNG CLUSTERED nếu tất cả các phần cột của định nghĩa chỉ mục được định nghĩa là KHÔNG NULL.
souplex

Câu trả lời:


27

Như @Souplex đã ám chỉ trong các bình luận, một lời giải thích có thể có thể là nếu cột này là cột có thể đầu tiên NULLtrong chỉ mục không được nhóm mà nó tham gia.

Đối với các thiết lập sau

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 hiển thị chỉ mục không được nhóm ixcó 248 trang lá và một trang gốc.

Một hàng điển hình trong trang lá chỉ mục trông giống như

nhập mô tả hình ảnh ở đây

Và trong trang gốc

nhập mô tả hình ảnh ở đây

Rồi chạy ...

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

Trả lại

+-----------------+--------------------+-------------+----------------+
|    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 |
+-----------------+--------------------+-------------+----------------+

Kiểm tra lá chỉ mục một lần nữa các hàng bây giờ trông giống như

nhập mô tả hình ảnh ở đây

và các hàng trong các trang cấp trên như bên dưới.

nhập mô tả hình ảnh ở đây

Mỗi hàng đã được cập nhật và hiện chứa hai byte cho số cột cùng với một byte khác cho NULL_BITMAP.

Do chiều rộng hàng thêm, chỉ mục không phân cụm hiện có 285 trang lá và hiện có hai trang cấp trung gian cùng với trang gốc.

Kế hoạch thực hiện cho

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

trông như sau

nhập mô tả hình ảnh ở đây

Điều này tạo ra một bản sao hoàn toàn mới của chỉ mục thay vì cập nhật bản hiện có và cần chia trang.


9

Nó chắc chắn sẽ tạo lại chỉ mục không được nhóm và không chỉ cập nhật siêu dữ liệu. Điều này đã được thử nghiệm trên SQL 2014 và thực sự không nên thử nghiệm trên hệ thống sản xuất:

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);

Và bây giờ là phần thú vị:

DBCC IND (0, z, -1)

Điều này sẽ cung cấp cho chúng tôi các trang cơ sở dữ liệu nơi bảng và chỉ mục không được nhóm được lưu trữ.

Tìm vị PagePIDtrí IndexIDlà 2 và PageTypelà 2, sau đó thực hiện các bước sau:

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

và sau đó:

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

Lưu ý rằng có một bitmap null trong tiêu đề:

Trích xuất tiêu đề trang

Bây giờ hãy làm:

alter table z alter Column b int null;

Nếu bạn thực sự thiếu kiên nhẫn, bạn có thể thử chạy lại dbcc pagelệnh nhưng nó sẽ thất bại, vì vậy hãy kiểm tra lại phân bổ một lần nữa DBCC IND (0, z, -1). Trang sẽ di chuyển như thể bằng phép thuật.

Vì vậy, việc thay đổi tính không hợp lệ của một cột sẽ ảnh hưởng đến việc lưu trữ các chỉ mục không được nhóm bao trùm cột đó, vì siêu dữ liệu cần được cập nhật và bạn không cần phải xây dựng lại các chỉ mục sau đó.


Nhiều ALTER TABLE ... ALTER COLUMN ...hoạt động có thể được thực hiện ONLINEbắt đầu với SQL Server 2016, nhưng:

ALTER TABLE (Transact-SQL)

  • Thay đổi một cột từ NOT NULLđến NULLkhông được hỗ trợ như một hoạt động trực tuyến khi cột bị thay đổi được tham chiếu bởi các chỉ mục không bao gồm.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.