2011-09-30 26 views
76

W dowolnym momencie w przeszłości, gdyby ktoś zapytał mnie o maksymalny rozmiar dla varchar(max), powiedziałbym 2 GB lub podniosłem dokładniej figure (2^31- 1 lub 2147483647).Maksymalny rozmiar zmiennej varchar (max)

Jednak w niektórych najnowszych badań, odkryłem, że varchar(max) zmienne mogą pozornie przekroczyć ten rozmiar:

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 

wyniki:

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

Tak, zważywszy, że teraz wiem, że zmienna może przekroczyć barierę 2GB - czy ktoś wie, jaki jest faktyczny limit dla zmiennej varchar(max)?


(. Powyższy test zakończony SQL Server 2008 (R2) nie byłbym zainteresowany, aby wiedzieć, czy ma ona zastosowanie do innych wersji)

+0

'declare @x varchar (max) = 'XX'; SELECT LEN (REPLICATE (@ x, 2147483647)) 'daje' 4294967294', ale długo trwa, nawet po powrocie 'SELECT', więc nie jestem pewien, co robi ten dodatkowy czas. –

Odpowiedz

62

O ile mogę powiedzieć, nie istnieje górna granica w 2008

w SQL Server 2005 kod w swoim pytaniu nie działa na przypisanie do zmiennej @GGMMsg z

Próba wzrost LOB poza maksymalnym dozwolonym rozmiarem 2 147 483 647 bajtów. Poniżej

kod nie z

replikacji: Długość wynik przekracza limitu długości (2 GB) z cel dużą czcionką.

Jednak wydaje się, że ograniczenia te zostały po cichu zniesione.Na 2008

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

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

SELECT LEN(@y) 

Zwraca

8589767761 

wpadłem to na moim 32-bitowym komputerze stacjonarnym więc ciąg 8GB jest sposób, w nadmiarze adresowalnych pamięci

Running

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

powrócił

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

Zakładam więc, że to wszystko zostanie zapisane na LOB stronach w tempdb bez sprawdzania poprawności długości. Wzrost liczby stron był powiązany z instrukcją SET @y = REPLICATE(@y,92681);. Początkowe przypisanie zmiennych do @y i obliczenie LEN nie zwiększyło tego.

Powodem tego jest fakt, że liczba stron jest znacznie większa, niż się spodziewałem. Zakładając stronę o wielkości 8 KB, daje to wynik 16,36 GB, co jest oczywiście mniej więcej podwójne, co wydaje się konieczne. Spekuluję, że jest to prawdopodobnie spowodowane nieefektywnością operacji łączenia ciągów, która wymaga skopiowania całego ogromnego ciągu i dołączenia fragmentu do końca, zamiast dodawać go do końca istniejącego łańcucha. Niestety w tej chwili metoda .WRITE dla zmiennych varchar (max).

Dodawanie

Ja również testowane zachowanie z złączenie nvarchar(max) + nvarchar(max) i nvarchar(max) + varchar(max). Oba pozwalają na przekroczenie limitu 2 GB. Próba następnie zapisania wyników tego w tabeli kończy się niepowodzeniem, ale ponownie pojawia się komunikat o błędzie Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.. Skrypt do tego jest poniżej (może zająć dużo czasu).

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

Doskonałe - więc wydaje się, że dokumentacja jest raczej "niekompletna" - zauważam, że zwykła strona odnosi się do maksymalnego "rozmiaru pamięci", który prawdopodobnie odnosi się tylko do kolumn, a nie do zmiennych. –

+0

@Damien - Zdecydowanie pojawia się w ten sposób. Nie jestem pewien, czy jest jakiś inny limit, który można osiągnąć w odniesieniu do całkowitej liczby stron, ale myślę, że jest to przechowywane w strukturze drzewa B (na podstawie p.381 SQL Server 2008), więc z zasady można go zdecydowanie rozszerzyć. –

+0

@Damien_The_Unbeliever - [Dokumentacja tutaj] (http://msdn.microsoft.com/en-us/library/ms345368.aspx) wydaje się nie pasować na podstawie eksperymentów tutaj, stwierdzając dość jednoznacznie, że "Duży obiekt (LOB) zmienne typu danych i parametry ... typy mogą mieć rozmiar do 2 GB " –

9

EDIT: Po dalszym dochodzeniu, mój oryginalny założenie, że to była anomalia (błąd?) składni declare @var datatype = value jest niepoprawna.

Zmodyfikowałem skrypt na 2005 rok, ponieważ ta składnia nie jest obsługiwana, a następnie wypróbowano zmodyfikowaną wersję w 2008 roku. W 2005 otrzymałem komunikat o błędzie Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.. W 2008 r. Zmodyfikowany skrypt nadal odnosi sukces.

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

Skrypt zawsze powoduje błąd (próbując wstawić tabelkę), ale w 2008 r. Zawsze otrzymuję jeden wynik w pierwszym zbiorze wyników, co oznacza, że ​​zmienna istnieje i jest dłuższa niż 2^31-1 długości . –

+0

@Damien_The_Unbeliever: Odciąłem skrypt do tylko części zmiennej i otrzymuję teraz takie same wyniki jak ty. W 2005 r. Otrzymałem komunikat 'Próba zwiększenia ...' w instrukcji 'set @GGMMsg = ...'. W 2008 roku scenariusz zakończył się sukcesem. –