Максимальный размер переменной varchar (max)

89

В любое время в прошлом, если бы меня спросили о максимальном размере a varchar(max), я бы сказал 2 ГБ или нашел более точную цифру (2 ^ 31-1 или 2147483647).

Однако в ходе недавнего тестирования я обнаружил, что varchar(max)переменные могут явно превышать этот размер:

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

Полученные результаты:

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

Итак, учитывая, что теперь я знаю, что переменная может превышать барьер в 2 ГБ - знает ли кто-нибудь, каков фактический предел для varchar(max)переменной?


(Вышеупомянутый тест завершен на SQL Server 2008 (не R2). Мне было бы интересно узнать, применимо ли оно к другим версиям)

Damien_The_Unbeliever
источник
declare @x varchar(max) = 'XX'; SELECT LEN(REPLICATE(@x,2147483647))дает 4294967294для меня, но требует много времени для запуска - даже после того, SELECTкак возвращается, поэтому не уверен, на что это дополнительное время тратится.
Мартин Смит

Ответы:

74

Насколько я могу судить, в 2008 году верхнего предела не было.

В SQL Server 2005 код в вашем вопросе не выполняется при назначении @GGMMsgпеременной с помощью

Попытка увеличить LOB сверх максимально допустимого размера в 2 147 483 647 байт.

приведенный ниже код не работает с

REPLICATE: длина результата превышает ограничение длины (2 ГБ) целевого большого типа.

Однако, похоже, эти ограничения были незаметно сняты. 2008 г.

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

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

SELECT LEN(@y) 

Возврат

8589767761

Я запускал это на своем 32-битном настольном компьютере, поэтому эта строка 8 ГБ намного превышает адресуемую память

Бег

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

Вернулся

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

поэтому я предполагаю, что все это просто сохраняется на LOBстраницах tempdbбез проверки длины. Рост количества страниц был связан с SET @y = REPLICATE(@y,92681);заявлением. Первоначальное присвоение переменной @yи LENрасчет этого не увеличили.

Причина упоминания этого заключается в том, что количество страниц намного больше, чем я ожидал. Предполагая, что страница размером 8 КБ, получается 16,36 ГБ, что, очевидно, более или менее вдвое больше, чем казалось бы необходимым. Я предполагаю, что это, вероятно, связано с неэффективностью операции конкатенации строк, требующей скопировать всю огромную строку и добавить кусок в конец, а не иметь возможность добавить в конец существующей строки. К сожалению, на данный момент этот .WRITEметод не поддерживается для переменных varchar (max).

Дополнение

Я также протестировал поведение с объединением nvarchar(max) + nvarchar(max)и nvarchar(max) + varchar(max). Оба из них позволяют превышать ограничение в 2 ГБ. Попытка затем сохранить результаты этого в таблице, но снова терпит неудачу с сообщением об ошибке Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.. Сценарий для этого ниже (запуск может занять много времени).

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
Отлично - поэтому может показаться, что документация довольно «неполная» - я отмечу, что обычная страница относится к максимальному «размеру хранилища», который предположительно применяется только к столбцам, а не к переменным.
Damien_The_Unbeliever
@Damien - Определенно так кажется. Не уверен, есть ли какой-то другой предел, который может быть достигнут с точки зрения общего количества страниц, но я думаю, что он хранится в древовидной структуре B (на основе стр. 381 внутренних компонентов SQL Server 2008), поэтому в принципе можно определенно расширить.
Мартин Смит
@Damien_The_Unbeliever - Документация здесь кажется совершенно неверной на основе проведенных здесь экспериментов, в которой довольно однозначно утверждается, что «переменные и параметры типа данных больших объектов (LOB) ... типы могут иметь размер до 2 ГБ»
Мартин Смит,
Вроде бесполезно, но интересно. Теоретически можно заполнить диск одной переменной ... :-)
gbn
У меня есть некоторые сомнения, потому что сохранение значения в переменной - это не то же самое, что сохранение его в столбце. Вы не хотите попробовать это с колонкой - или у вас есть обновление? Даже SQL Server 2000 может иметь varcharзначения длиннее 8000 символов в буквальных строках кода, если вы не пытаетесь поместить их в переменную или varcharстолбец.
ErikE
9

РЕДАКТИРОВАТЬ : после дальнейшего расследования мое первоначальное предположение, что это была аномалия (ошибка?) declare @var datatype = valueСинтаксиса, неверно.

Я изменил ваш сценарий для 2005 года, так как этот синтаксис не поддерживается, затем попробовал модифицированную версию для 2008 года. В 2005 году я получаю Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.сообщение об ошибке. В 2008 году модифицированный сценарий все еще успешно работает.

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)
Джо Стефанелли
источник
Сценарий всегда выдает ошибку (при попытке выполнить вставку таблицы), но в 2008 году я всегда получаю один результат в первом наборе результатов, что указывает на то, что переменная действительно существует и имеет длину более 2 ^ 31-1.
Damien_The_Unbeliever
@Damien_The_Unbeliever: Я сократил сценарий до переменной части и теперь получаю те же результаты, что и вы. В 2005 году я получаю Attempting to grow...ошибку в set @GGMMsg=...заявлении. В 2008 году сценарий успешен.
Джо Стефанелли