Максимальний розмір змінної varchar (max)


89

У будь-який час раніше, якби хтось запитував у мене максимальний розмір для 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). Мені буде цікаво дізнатись, чи стосується це інших версій)


declare @x varchar(max) = 'XX'; SELECT LEN(REPLICATE(@x,2147483647))дає 4294967294для мене, але працює багато часу - навіть після того, SELECTяк повернувся, тому не знаю, на що витрачається зайвий час.
Martin Smith

Відповіді:


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), тому в принципі його можна однозначно розширити.
Martin Smith

@Damien_The_Unbeliever - Документація тут здається абсолютно невірною на основі проведених тут експериментів, де досить однозначно стверджується, що "Змінні та параметри типу даних великого об'єкта (LOB) ... можуть мати розмір до 2 ГБ"
Мартін Сміт,

Хоч і марно, але цікаво. Теоретично ви можете заповнити диск однією змінною ... :-)
gbn

У мене є деякі вагання тут, тому що зберігати значення у змінній - це не те саме, що зберігати його у стовпці. Вам хочеться спробувати це за допомогою стовпця - або у вас є оновлення? Навіть SQL Server 2000 міг мати varcharзначення, що перевищують 8000 символів, у буквальних рядках коду, якщо ви не намагалися помістити його у змінну чи varcharстовпець.
ErikE

9

EDIT : Після подальшого дослідження моє початкове припущення, що це була аномалія (помилка?) 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 році сценарій вдався.
Джо Стефанеллі,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.