Kích thước tối đa của một biến varchar (max)


88

Vào bất kỳ thời điểm nào trước đây, nếu ai đó hỏi tôi kích thước tối đa cho a varchar(max), tôi sẽ trả lời là 2GB hoặc tra cứu một con số chính xác hơn (2 ^ 31-1, hoặc 2147483647).

Tuy nhiên, trong một số thử nghiệm gần đây, tôi phát hiện ra rằng varchar(max)các biến dường như có thể vượt quá kích thước này:

create table T (
    Val1 varchar(max) not null
)
go
declare @KMsg varchar(max) = REPLICATE('a',1024);
declare @MMsg varchar(max) = REPLICATE(@KMsg,1024);
declare @GMsg varchar(max) = REPLICATE(@MMsg,1024);
declare @GGMMsg varchar(max) = @GMsg + @GMsg + @MMsg;
select LEN(@GGMMsg)
insert into T(Val1) select @GGMMsg
select LEN(Val1) from T

Các kết quả:

(no column name)
2148532224
(1 row(s) affected)
Msg 7119, Level 16, State 1, Line 6
Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.
The statement has been terminated.

(no column name)
(0 row(s) affected)

Vì vậy, bây giờ tôi biết rằng một biến có thể vượt quá rào cản 2GB - có ai biết giới hạn thực tế cho một varchar(max)biến là bao nhiêu không?


(Kiểm tra ở trên đã hoàn thành trên SQL Server 2008 (không phải R2). Tôi muốn biết liệu nó có áp dụng cho các phiên bản khác hay không)


declare @x varchar(max) = 'XX'; SELECT LEN(REPLICATE(@x,2147483647))mang lại 4294967294cho tôi nhưng mất nhiều thời gian để chạy - ngay cả sau khi SELECTđã quay trở lại, vì vậy không chắc chắn rằng thời gian thêm đó được dành để làm gì.
Martin Smith

Câu trả lời:


73

Theo như tôi có thể nói thì không có giới hạn trên trong năm 2008.

Trong SQL Server 2005, mã trong câu hỏi của bạn không thành công khi gán cho @GGMMsgbiến với

Đang cố gắng tăng LOB vượt quá kích thước tối đa cho phép là 2.147.483.647 byte.

mã bên dưới không thành công với

REPLICATE: Độ dài của kết quả vượt quá giới hạn độ dài (2GB) của loại lớn mục tiêu.

Tuy nhiên, có vẻ như những hạn chế này đã lặng lẽ được dỡ bỏ. Vào năm 2008

DECLARE @y VARCHAR(MAX) = REPLICATE(CAST('X' AS VARCHAR(MAX)),92681); 

SET @y = REPLICATE(@y,92681);

SELECT LEN(@y) 

Lợi nhuận

8589767761

Tôi đã chạy điều này trên máy tính để bàn 32 bit của mình nên chuỗi 8GB này vượt quá bộ nhớ địa chỉ

Đang chạy

select internal_objects_alloc_page_count
from sys.dm_db_task_space_usage
WHERE session_id = @@spid

Trả lại

internal_objects_alloc_page_co 
------------------------------ 
2144456    

vì vậy tôi cho rằng tất cả điều này chỉ được lưu trữ trong LOBcác trang tempdbmà không có xác nhận về độ dài. Tất cả sự tăng trưởng về số lượng trang đều được kết hợp với SET @y = REPLICATE(@y,92681);tuyên bố. Việc gán biến ban đầu cho @yLENphép tính không làm tăng điều này.

Lý do đề cập đến điều này là vì số lượng trang nhiều hơn tôi mong đợi. Giả sử một trang 8KB thì điều này hoạt động ở 16,36 GB, rõ ràng là nhiều hơn hoặc ít hơn gấp đôi những gì có vẻ là cần thiết. Tôi suy đoán rằng điều này có thể là do sự kém hiệu quả của hoạt động nối chuỗi cần sao chép toàn bộ chuỗi lớn và nối một đoạn vào cuối thay vì có thể thêm vào cuối chuỗi hiện có. Rất tiếc tại thời điểm này, .WRITEphương thức này không được hỗ trợ cho các biến varchar (max).

Thêm vào

Tôi cũng đã thử nghiệm hành vi với nối nvarchar(max) + nvarchar(max)nvarchar(max) + varchar(max). Cả hai đều cho phép vượt quá giới hạn 2GB. Sau đó cố gắng lưu trữ kết quả của điều này trong một bảng nhưng không thành công với thông báo lỗi Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.một lần nữa. Tập lệnh cho điều đó ở bên dưới (có thể mất nhiều thời gian để chạy).

DECLARE @y1 VARCHAR(MAX) = REPLICATE(CAST('X' AS VARCHAR(MAX)),2147483647); 
SET @y1 = @y1 + @y1;
SELECT LEN(@y1), DATALENGTH(@y1)  /*4294967294, 4294967292*/


DECLARE @y2 NVARCHAR(MAX) = REPLICATE(CAST('X' AS NVARCHAR(MAX)),1073741823); 
SET @y2 = @y2 + @y2;
SELECT LEN(@y2), DATALENGTH(@y2)  /*2147483646, 4294967292*/


DECLARE @y3 NVARCHAR(MAX) = @y2 + @y1
SELECT LEN(@y3), DATALENGTH(@y3)   /*6442450940, 12884901880*/

/*This attempt fails*/
SELECT @y1 y1, @y2 y2, @y3 y3
INTO Test

1
Tuyệt vời - vì vậy có vẻ như tài liệu này khá "không đầy đủ" - tôi lưu ý rằng trang thông thường đề cập đến "kích thước lưu trữ" tối đa, có lẽ chỉ áp dụng cho các cột, không phải biến.
Damien_The_Un Believer 30/09/11

@Damien - Chắc chắn xuất hiện theo cách đó. Không chắc liệu có giới hạn nào khác có thể đạt đến về tổng số trang hay không nhưng tôi nghĩ điều này được lưu trữ trong cấu trúc cây B (dựa trên p.381 của SQL Server 2008 nội bộ) nên về nguyên tắc có thể được mở rộng chắc chắn.
Martin Smith

@Damien_The_Un Believer - Tài liệu ở đây có vẻ sai hoàn toàn dựa trên các thử nghiệm ở đây, nêu khá rõ ràng rằng "Các biến và tham số kiểu dữ liệu đối tượng lớn (LOB) ... các loại có thể có kích thước lên đến 2 GB"
Martin Smith

Kinda mặc dù vô dụng nhưng thú vị. Về lý thuyết, bạn có thể điền vào đĩa với một biến ... :-)
GBN

Tôi có một số do dự ở đây vì lưu trữ một giá trị trong một biến không giống như lưu trữ nó trong một cột. Thay vào đó, bạn có muốn thử điều này với một cột - hay bạn có bản cập nhật? Ngay cả SQL Server 2000 cũng có thể có varcharcác giá trị dài hơn 8000 ký tự trong chuỗi ký tự trong mã, miễn là bạn không cố đặt nó vào một biến hoặc varcharcột.
ErikE

9

CHỈNH SỬA : Sau khi điều tra thêm, giả định ban đầu của tôi rằng đây là một sự bất thường (lỗi?) Của declare @var datatype = valuecú pháp là không chính xác.

Tôi đã sửa đổi tập lệnh của bạn cho năm 2005 vì cú pháp đó không được hỗ trợ, sau đó thử phiên bản sửa đổi vào năm 2008. Vào năm 2005, tôi nhận được thông Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.báo lỗi. Năm 2008, kịch bản sửa đổi vẫn thành công.

declare @KMsg varchar(max); set @KMsg = REPLICATE('a',1024);
declare @MMsg varchar(max); set @MMsg = REPLICATE(@KMsg,1024);
declare @GMsg varchar(max); set @GMsg = REPLICATE(@MMsg,1024);
declare @GGMMsg varchar(max); set @GGMMsg = @GMsg + @GMsg + @MMsg;
select LEN(@GGMMsg)

Tập lệnh luôn tạo ra lỗi (bằng cách cố gắng thực hiện chèn bảng), nhưng vào năm 2008, tôi luôn nhận được một kết quả trong tập kết quả đầu tiên, cho biết rằng biến có tồn tại và có độ dài hơn 2 ^ 31-1.
Damien_The_Un Believer

@Damien_The_Un Believer: Tôi đã cắt kịch bản xuống chỉ phần biến và bây giờ nhận được kết quả giống như bạn. Vào năm 2005, tôi nhận được Attempting to grow...lỗi trong set @GGMMsg=...báo cáo. Năm 2008, kịch bản thành công.
Joe Stefanelli
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.