ALTER TABLE ... ALTER COLUMN
lệnh rất mạnh mẽ. Bạn có thể sử dụng nó để thay đổi kiểu dữ liệu, độ dài, độ chính xác, tỷ lệ, khả năng vô hiệu, đối chiếu ... và nhiều thứ khác của cột.
Nó chắc chắn thuận tiện hơn so với lựa chọn thay thế:Tạo một bảng mới và di chuyển dữ liệu mỗi khi cần thay đổi. Tuy nhiên, chỉ có rất nhiều điều có thể được thực hiện để che giấu sự phức tạp tiềm ẩn. Cùng với một số lượng lớn các hạn chế về những gì thậm chí có thể có với lệnh này, luôn có câu hỏi về hiệu suất.
Cuối cùng, các bảng được lưu trữ dưới dạng một chuỗi byte với một số siêu dữ liệu ở nơi khác trong hệ thống để mô tả ý nghĩa của mỗi byte đó và cách chúng liên quan đến từng cột khác nhau của bảng. Khi chúng tôi yêu cầu SQL Server thay đổi một số khía cạnh trong định nghĩa của cột, nó cần phải kiểm tra xem dữ liệu hiện có có tương thích với định nghĩa mới hay không. Nó cũng cần xác định xem bố cục vật lý hiện tại có cần thay đổi hay không.
Tùy thuộc vào loại thay đổi và cấu hình của cơ sở dữ liệu, một ALTER COLUMN
lệnh sẽ cần thực hiện một trong các hành động sau:
- Chỉ thay đổi siêu dữ liệu trong bảng hệ thống.
- Kiểm tra tính tương thích của tất cả dữ liệu hiện có, sau đó thay đổi siêu dữ liệu.
- Viết lại một số hoặc tất cả dữ liệu được lưu trữ để phù hợp với định nghĩa mới.
Tùy chọn 1 thể hiện trường hợp lý tưởng theo quan điểm hiệu suất. Nó chỉ yêu cầu một số thay đổi đối với bảng hệ thống và một số lượng ghi nhật ký tối thiểu. Thao tác sẽ vẫn yêu cầu sửa đổi giản đồ hạn chế Sch-M
khóa, nhưng các thay đổi siêu dữ liệu sẽ tự hoàn thành rất nhanh, bất kể kích thước của bảng.
Thay đổi Chỉ siêu dữ liệu
Có một số trường hợp đặc biệt cần chú ý, nhưng như một bản tóm tắt chung, các hành động sau chỉ yêu cầu thay đổi siêu dữ liệu:
- Đi từ
NOT NULL
thànhNULL
cho cùng một loại dữ liệu. - Tăng kích thước tối đa của
varchar
,nvarchar
hoặcvarbinary
cột (ngoại trừmax
).
Cải tiến trong SQL Server 2016
Chủ đề của bài đăng này là các thay đổi bổ sung được bật chỉ dành cho siêu dữ liệu từ SQL Server 2016 trở đi . Không cần thay đổi cú pháp và không cần sửa đổi cài đặt cấu hình. Bạn nhận được những cải tiến không có giấy tờ này miễn phí.
Các khả năng mới nhắm mục tiêu một tập hợp con có độ dài cố định Loại dữ liệu. Các khả năng mới áp dụng cho các bảng lưu trữ hàng trong các trường hợp sau:
- Nén phải được bật:
- Trên tất cả các chỉ mục và phân vùng , bao gồm cả đống cơ sở hoặc chỉ mục nhóm.
- Một trong hai
ROW
hoặcPAGE
nén. - Chỉ mục và phân vùng có thể sử dụng hỗn hợp của các mức nén này. Điều quan trọng là không có chỉ mục hoặc phân vùng chưa được nén.
- Thay đổi từ
NULL
thànhNOT NULL
không được phép . - Thay đổi kiểu số nguyên sau đây được hỗ trợ:
-
smallint
thànhinteger
hoặcbigint
. -
integer
thànhbigint
. -
smallmoney
thànhmoney
(sử dụng biểu diễn số nguyên trong nội bộ).
-
- Thay đổi kiểu chuỗi và nhị phân sau đây được hỗ trợ:
-
char(n)
thànhchar(m)
hoặcvarchar(m)
-
nchar(n)
thànhnchar(m)
hoặcnvarchar(m)
-
binary(n)
thànhbinary(m)
hoặcvarbinary(m)
- Tất cả những điều trên chỉ dành cho
n < m
vàm != max
- Thay đổi đối chiếu không được phép
-
Những thay đổi này chỉ có thể là siêu dữ liệu vì bố cục dữ liệu nhị phân bên dưới không thay đổi khi Trình mô tả cột định dạng hàng được sử dụng (do đó cần phải nén). Không có nén, kho lưu trữ hàng sử dụng FixedVar ban đầu biểu diễn, không thể đáp ứng các thay đổi kiểu dữ liệu có độ dài cố định này nếu không viết lại bố cục vật lý.
Bạn có thể nhận thấy rằng tinyint
bị bỏ qua khỏi danh sách kiểu số nguyên. Điều này là do nó chưa được ký, trong khi các kiểu số nguyên khác đều được ký, vì vậy không thể thực hiện thay đổi chỉ siêu dữ liệu. Ví dụ:giá trị 255 có thể vừa với một byte cho tinyint
, nhưng yêu cầu hai byte ở bất kỳ định dạng đã ký nào. Các định dạng đã ký có thể giữ từ -128 đến +127 trong một byte khi nén.
Ví dụ về Số nguyên
Một ứng dụng rất hữu ích của cải tiến này là thay đổi kiểu dữ liệu của cột bằng IDENTITY
tài sản.
Giả sử chúng ta có bảng heap sau bằng cách sử dụng nén hàng (nén trang cũng sẽ hoạt động):
DROP TABLE IF EXISTS dbo.Test; GO CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL, some_value integer NOT NULL ) WITH (DATA_COMPRESSION = ROW);
Hãy thêm 5 triệu hàng dữ liệu. Điều này đủ để làm cho nó rõ ràng (từ quan điểm hiệu suất) cho dù việc thay đổi kiểu dữ liệu cột có phải là một hoạt động chỉ siêu dữ liệu hay không:
WITH Numbers AS ( SELECT n = ROW_NUMBER() OVER (ORDER BY @@SPID) FROM sys.all_columns AS AC1 CROSS JOIN sys.all_columns AS AC2 ORDER BY n OFFSET 0 ROWS FETCH FIRST 5 * 1000 * 1000 ROWS ONLY ) INSERT dbo.Test WITH (TABLOCKX) ( some_value ) SELECT N.n FROM Numbers AS N;
Tiếp theo, chúng tôi sẽ gửi lại IDENTITY
để làm cho nó có vẻ như chúng ta gần như sắp hết các giá trị sẽ phù hợp với một số nguyên integer
:
DBCC CHECKIDENT ( N'dbo.Test', RESEED, 2147483646 );
Chúng tôi có thể thêm một hàng thành công:
INSERT dbo.Test (some_value) VALUES (123456);
Nhưng đang cố thêm một hàng khác:
INSERT dbo.Test (some_value) VALUES (7890);
Kết quả là một thông báo lỗi:
Msg 8115, Mức 16, Trạng thái 1, Dòng 1Lỗi tràn số học khi chuyển đổi IDENTITY thành kiểu dữ liệu int.
Chúng tôi có thể khắc phục điều đó bằng cách chuyển đổi cột thành bigint
:
ALTER TABLE dbo.Test ALTER COLUMN id bigint NOT NULL;
Nhờ những cải tiến trong SQL Server 2016, lệnh này thay đổi chỉ siêu dữ liệu và hoàn thành ngay lập tức. INSERT
trước hiện đã hoàn tất thành công câu lệnh (câu lệnh gây ra lỗi tràn số học).
Khả năng mới này không giải quyết được tất cả các vấn đề xung quanh việc thay đổi loại cột với IDENTITY
bất động sản. Chúng tôi sẽ vẫn cần xóa và tạo lại bất kỳ chỉ mục nào trên cột, tạo lại bất kỳ khóa ngoại tham chiếu nào, v.v. Điều đó hơi nằm ngoài phạm vi của bài đăng này (mặc dù Aaron Bertrand đã viết về nó trước đây). Có thể thay đổi loại dưới dạng hoạt động chỉ siêu dữ liệu chắc chắn không ảnh hưởng gì. Với việc lập kế hoạch cẩn thận, các bước cần thiết khác có thể được thực hiện hiệu quả nhất có thể, chẳng hạn như sử dụng ghi nhật ký tối thiểu hoặc ONLINE
hoạt động.
Cẩn thận với cú pháp
Đảm bảo luôn luôn chỉ định NULL
hoặc NOT NULL
khi thay đổi kiểu dữ liệu với ALTER COLUMN
. Ví dụ, chúng tôi cũng muốn thay đổi kiểu dữ liệu của some_value
trong bảng kiểm tra của chúng tôi từ integer NOT NULL
thành bigint NOT NULL
.
Khi chúng tôi viết lệnh, chúng tôi bỏ qua NULL
hoặc NOT NULL
vòng loại:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint;
Lệnh này hoàn tất thành công dưới dạng thay đổi chỉ siêu dữ liệu, nhưng cũng xóa NOT NULL
hạn chế. Cột bây giờ là bigint NULL
, đó không phải là những gì chúng tôi dự định. Hành vi này đã được ghi lại, nhưng rất dễ bị bỏ qua.
Chúng tôi có thể cố gắng sửa chữa lỗi của mình bằng:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint NOT NULL;
Đây không một thay đổi chỉ siêu dữ liệu. Chúng tôi không được phép thay đổi từ NULL
thành NOT NULL
(xem lại bảng trước đó nếu bạn cần cập nhật các điều kiện). SQL Server sẽ cần phải kiểm tra tất cả các giá trị hiện có để đảm bảo không có giá trị nào. Sau đó, nó sẽ viết lại mọi hàng về mặt vật lý của cái bàn. Cũng như bản thân nó chậm, những hành động này tạo ra rất nhiều nhật ký giao dịch, có thể có tác dụng phụ.
Xin lưu ý thêm, lỗi tương tự này không thể xảy ra đối với các cột có IDENTITY
bất động sản. Nếu chúng ta viết ALTER COLUMN
câu lệnh không có NULL
hoặc NOT NULL
trong trường hợp đó, công cụ giả định một cách hữu ích rằng chúng tôi có ý NOT NULL
bởi vì thuộc tính nhận dạng không được phép trên các cột có thể rỗng. Tốt hơn hết là không nên dựa vào hành vi này.
Luôn chỉ định NULL
hoặc NOT NULL
với ALTER COLUMN
.
Đối chiếu
Cần đặc biệt cẩn thận khi thay đổi cột chuỗi có đối chiếu không khớp với giá trị mặc định cho cơ sở dữ liệu.
Ví dụ:giả sử chúng ta có một bảng với đối chiếu phân biệt chữ hoa chữ thường và trọng âm (giả sử mặc định của cơ sở dữ liệu là khác):
DROP TABLE IF EXISTS dbo.Test2; GO CREATE TABLE dbo.Test2 ( id integer IDENTITY NOT NULL, some_string char(8) COLLATE Latin1_General_100_CS_AS NOT NULL ) WITH (DATA_COMPRESSION = ROW);
Thêm 5 triệu hàng dữ liệu:
WITH Numbers AS ( SELECT n = ROW_NUMBER() OVER (ORDER BY @@SPID) FROM sys.all_columns AS AC1 CROSS JOIN sys.all_columns AS AC2 ORDER BY n OFFSET 0 ROWS FETCH FIRST 5 * 1000 * 1000 ROWS ONLY ) INSERT dbo.Test2 WITH (TABLOCKX) ( some_string ) SELECT CONVERT(char(8), N.n) COLLATE Latin1_General_100_CS_AS FROM Numbers AS N;
Nhân đôi độ dài của cột chuỗi bằng lệnh sau:
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) NOT NULL;
Chúng tôi nhớ chỉ định NOT NULL
, nhưng quên về đối chiếu không mặc định. SQL Server giả định rằng chúng tôi muốn thay đổi đối chiếu thành mặc định của cơ sở dữ liệu (Latin1_General_CI_AS
cho cơ sở dữ liệu thử nghiệm của tôi). Việc thay đổi đối chiếu ngăn hoạt động chỉ ở chế độ siêu dữ liệu và do đó, hoạt động sẽ chạy trong vài phút, tạo ra đống nhật ký.
Tạo lại bảng và dữ liệu bằng cách sử dụng tập lệnh trước, sau đó thử ALTER COLUMN
lệnh một lần nữa, nhưng chỉ định đối chiếu không mặc định hiện có như một phần của lệnh:
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;
Thay đổi hiện hoàn tất ngay lập tức, dưới dạng hoạt động chỉ siêu dữ liệu. Như với NULL
và NOT NULL
cú pháp, nó trả tiền để được rõ ràng để tránh tai nạn. Đây là lời khuyên tốt nói chung, không chỉ cho ALTER COLUMN
.
Nén
Xin lưu ý rằng việc nén cần phải được chỉ định rõ ràng cho từng chỉ mục và riêng cho bảng cơ sở nếu đó là một đống. Đây là một ví dụ khác trong đó việc sử dụng cú pháp hoặc phím tắt viết tắt có thể ngăn cản kết quả mong muốn.
Ví dụ:bảng sau không chỉ định nén rõ ràng cho khóa chính hoặc định nghĩa chỉ mục nội dòng:
CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL PRIMARY KEY, some_value integer NOT NULL INDEX [IX dbo.Test some_value] ) WITH (DATA_COMPRESSION = PAGE);
PRIMARY KEY
sẽ được gán tên, mặc định là CLUSTERED
và là PAGE
bị nén. Chỉ mục nội dòng sẽ là NONCLUSTERED
và hoàn toàn không bị nén. Bảng này sẽ không được bật cho bất kỳ tối ưu hóa mới nào vì không phải tất cả các chỉ mục và phân vùng đều được nén.
Một định nghĩa bảng tốt hơn và rõ ràng hơn sẽ là:
CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL CONSTRAINT [PK dbo.Test id] PRIMARY KEY CLUSTERED WITH (DATA_COMPRESSION = PAGE), some_value integer NOT NULL INDEX [IX dbo.Test some_value] NONCLUSTERED WITH (DATA_COMPRESSION = ROW) );
Bảng này sẽ đủ điều kiện cho các tối ưu hóa mới vì tất cả các chỉ mục và phân vùng đều được nén. Như đã lưu ý trước đây, trộn các kiểu nén là tốt.
Có nhiều cách để viết CREATE TABLE
này tuyên bố một cách rõ ràng, do đó, có một yếu tố sở thích cá nhân. Điểm rút ra quan trọng là luôn rõ ràng về những gì bạn muốn. Điều này áp dụng cho CREATE INDEX
riêng biệt cũng như các câu lệnh.
Sự kiện mở rộng và Cờ theo dõi
Có một sự kiện mở rộng dành riêng cho ALTER COLUMN
chỉ siêu dữ liệu mới các hoạt động được hỗ trợ trong SQL Server 2016 trở đi.
Sự kiện mở rộng là compressed_alter_column_is_md_only
trong Gỡ lỗi kênh. Các trường sự kiện của nó là object_id
, column_id
và is_md_only
(true / false).
Sự kiện này chỉ cho biết nếu một thao tác chỉ là siêu dữ liệu do các khả năng mới của SQL Server 2016. Các thay đổi cột chỉ dành cho siêu dữ liệu trước năm 2016 sẽ hiển thị is_md_only = false
mặc dù vẫn chỉ là siêu dữ liệu.
Các sự kiện mở rộng khác hữu ích để theo dõi ALTER COLUMN
hoạt động bao gồm metadata_ddl_alter_column
và alter_column_event
, cả trong Analytic kênh.
Bạn có cần phải tắt các khả năng mới của SQL Server 2016 vì bất kỳ lý do gì, cờ theo dõi toàn cầu (hoặc khởi động) 3618 không có tài liệu có thể được sử dụng. Cờ theo dõi này không hiệu quả khi được sử dụng ở cấp phiên. Không có cách nào để chỉ định cờ theo dõi cấp truy vấn với ALTER COLUMN
lệnh.
Kết luận
Có thể thay đổi một số kiểu dữ liệu số nguyên có độ dài cố định bằng thay đổi chỉ siêu dữ liệu là một cải tiến sản phẩm rất đáng hoan nghênh. Nó yêu cầu bảng đã được nén hoàn toàn, nhưng dù sao thì đó cũng đang trở thành một điều phổ biến. Điều này đặc biệt đúng vì tính năng nén được bật trong tất cả các phiên bản bắt đầu với SQL Server 2016 Gói Dịch vụ 1.
Các cột kiểu chuỗi có độ dài cố định có lẽ ít phổ biến hơn nhiều. Một số điều này có thể là do những cân nhắc hơi lạc hậu như sử dụng dung lượng. Khi được nén, các cột chuỗi có độ dài cố định không lưu trữ các khoảng trống ở cuối, làm cho chúng hiệu quả như các cột chuỗi có độ dài thay đổi theo quan điểm lưu trữ. Việc cắt bớt không gian để thao tác hoặc hiển thị có thể gây khó chịu, nhưng nếu dữ liệu thường chiếm hầu hết độ dài tối đa, thì các loại độ dài cố định có thể có những lợi thế quan trọng, đặc biệt là liên quan đến việc cấp bộ nhớ cho những thứ như sắp xếp và băm.
Không phải tất cả đều là tin tốt khi tính năng nén được bật. Tôi đã đề cập trước đó rằng SQL Server đôi khi có thể thực hiện thay đổi chỉ siêu dữ liệu sau khi kiểm tra rằng tất cả các giá trị hiện có sẽ chuyển đổi thành công sang kiểu mới. Đây là trường hợp khi sử dụng ALTER COLUMN
để thay đổi từ integer
thành smallint
Ví dụ. Rất tiếc, các thao tác này hiện không chỉ dành cho siêu dữ liệu dành cho các đối tượng nén.
Lời cảm ơn
Đặc biệt cảm ơn Panagiotis Antonopoulos (Kỹ sư phần mềm chính) và Mirek Sztajno (Người quản lý chương trình cấp cao) từ nhóm sản phẩm SQL Server để được hỗ trợ và hướng dẫn trong quá trình nghiên cứu và viết bài này.
Không có chi tiết nào được đưa ra trong tác phẩm này nên được coi là tài liệu chính thức của Microsoft hoặc tuyên bố sản phẩm.