Maximale Größe einer varchar (max) -Variablen

88

Wenn mich in der Vergangenheit jemand nach der maximalen Größe für a gefragt hätte varchar(max), hätte ich 2 GB gesagt oder eine genauere Zahl nachgeschlagen (2 ^ 31-1 oder 2147483647).

Bei einigen kürzlich durchgeführten Tests habe ich jedoch festgestellt, dass varchar(max)Variablen diese Größe anscheinend überschreiten können:

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

Ergebnisse:

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

Angesichts der Tatsache , dass ich jetzt weiß, dass eine Variable die 2-GB-Grenze überschreiten kann, weiß jemand, wie hoch das tatsächliche Limit für eine varchar(max)Variable ist?


(Der obige Test wurde unter SQL Server 2008 (nicht R2) abgeschlossen. Es würde mich interessieren, ob er für andere Versionen gilt.)

Damien_The_Unbeliever
quelle
declare @x varchar(max) = 'XX'; SELECT LEN(REPLICATE(@x,2147483647))gibt 4294967294für mich aber dauert lange zu laufen - auch nachdem der SELECTzurückgekehrt ist, also nicht sicher, was diese zusätzliche Zeit damit verbracht wird.
Martin Smith

Antworten:

72

Soweit ich das beurteilen kann, gibt es 2008 keine Obergrenze.

In SQL Server 2005 schlägt der Code in Ihrer Frage bei der Zuordnung zur @GGMMsgVariablen mit fehl

Der Versuch, LOB über die maximal zulässige Größe von 2.147.483.647 Bytes hinaus zu vergrößern.

Der folgende Code schlägt mit fehl

REPLICATE: Die Länge des Ergebnisses überschreitet die Längenbeschränkung (2 GB) des großen Zieltyps.

Es scheint jedoch, dass diese Einschränkungen stillschweigend aufgehoben wurden. Im Jahr 2008

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

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

SELECT LEN(@y) 

Kehrt zurück

8589767761

Ich habe dies auf meinem 32-Bit-Desktop-Computer ausgeführt, sodass diese 8-GB-Zeichenfolge den adressierbaren Speicher weit übersteigt

Laufen

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

Ist zurückgekommen

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

Ich gehe also davon aus, dass dies alles nur auf LOBSeiten tempdbohne Validierung der Länge gespeichert wird . Das Wachstum der Seitenzahl war alle mit der SET @y = REPLICATE(@y,92681);Aussage verbunden. Die anfängliche Variablenzuordnung zu @yund die LENBerechnung haben dies nicht erhöht.

Der Grund für die Erwähnung ist, dass die Seitenzahl enorm höher ist als ich erwartet hatte. Unter der Annahme einer 8-KB-Seite ergibt sich eine Größe von 16,36 GB, was offensichtlich mehr oder weniger doppelt so hoch ist, wie es notwendig erscheint. Ich spekuliere, dass dies wahrscheinlich auf die Ineffizienz der Zeichenfolgenverkettungsoperation zurückzuführen ist, bei der die gesamte große Zeichenfolge kopiert und ein Teil an das Ende angehängt werden muss, anstatt das Ende der vorhandenen Zeichenfolge hinzufügen zu können. Leider wird die .WRITEMethode derzeit nicht für varchar (max) -Variablen unterstützt .

Zusatz

Ich habe auch das Verhalten mit Verkettung nvarchar(max) + nvarchar(max)und getestet nvarchar(max) + varchar(max). In beiden Fällen kann das 2-GB-Limit überschritten werden. Der Versuch, die Ergebnisse davon in einer Tabelle zu speichern, schlägt jedoch mit der Fehlermeldung Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.erneut fehl . Das Skript dafür finden Sie weiter unten (die Ausführung kann lange dauern).

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
Martin Smith
quelle
1
Ausgezeichnet - so scheint es, dass die Dokumentation eher "unvollständig" ist - ich stelle fest, dass sich die übliche Seite auf eine maximale "Speichergröße" bezieht, die vermutlich nur für Spalten gilt, nicht für Variablen.
Damien_The_Unbeliever
@ Damien - Erscheint definitiv so. Ich bin mir nicht sicher, ob es eine andere Grenze gibt, die in Bezug auf die Gesamtzahl der Seiten erreicht werden kann, aber ich denke, diese ist in einer B-Baumstruktur gespeichert (basierend auf S.381 der SQL Server 2008-Interna), sodass sie im Prinzip definitiv erweitert werden könnte.
Martin Smith
@Damien_The_Unbeliever - Die Dokumentation hier scheint auf der Grundlage der Experimente hier völlig falsch zu sein und besagt ziemlich eindeutig, dass "Variablen und Parameter des Datentyps für große Objekte (LOB) ... bis zu 2 GB groß sein können"
Martin Smith
Ein bisschen nutzlos, aber interessant. Theoretisch könnte man die Platte mit einer Variablen füllen ... :-)
gbn
Ich zögere hier, weil das Speichern eines Werts in einer Variablen nicht dasselbe ist wie das Speichern in einer Spalte. Möchten Sie dies stattdessen mit einer Spalte versuchen - oder haben Sie ein Update? Sogar SQL Server 2000 kann varcharWerte enthalten, die länger als 8000 Zeichen in Literalzeichenfolgen im Code sind, solange Sie nicht versucht haben, sie in eine Variable oder varcharSpalte einzufügen.
ErikE
9

EDIT : Nach weiteren Untersuchungen ist meine ursprüngliche Annahme, dass dies eine Anomalie (Fehler?) Der declare @var datatype = valueSyntax war, falsch.

Ich habe Ihr Skript für 2005 geändert, da diese Syntax nicht unterstützt wird, und dann 2008 die geänderte Version ausprobiert. 2005 wird die Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.Fehlermeldung angezeigt. Im Jahr 2008 ist das geänderte Skript immer noch erfolgreich.

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)
Joe Stefanelli
quelle
Das Skript erzeugt immer einen Fehler (durch den Versuch, die Tabelle einzufügen), aber 2008 erhalte ich immer ein Ergebnis in der ersten Ergebnismenge, das angibt, dass die Variable vorhanden ist und länger als 2 ^ 31-1 ist.
Damien_The_Unbeliever
@Damien_The_Unbeliever: Ich habe das Skript auf den variablen Teil reduziert und erhalte jetzt die gleichen Ergebnisse wie Sie. Im Jahr 2005 erhalte ich den Attempting to grow...Fehler auf der set @GGMMsg=...Erklärung. Im Jahr 2008 ist das Skript erfolgreich.
Joe Stefanelli