Dimensione massima di una variabile varchar (max)


90

In qualsiasi momento in passato, se qualcuno mi avesse chiesto la dimensione massima per un varchar(max), avrei detto 2 GB o cercato una cifra più esatta (2 ^ 31-1 o 2147483647).

Tuttavia, in alcuni test recenti, ho scoperto che le varchar(max)variabili possono apparentemente superare questa dimensione:

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

Risultati:

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

Quindi, dato che ora so che una variabile può superare la barriera di 2 GB, qualcuno sa qual è il limite effettivo per una varchar(max)variabile?


(Test sopra completato su SQL Server 2008 (non R2). Sarei interessato a sapere se si applica ad altre versioni)


declare @x varchar(max) = 'XX'; SELECT LEN(REPLICATE(@x,2147483647))4294967294per me ma impiega molto tempo a correre, anche dopo che SELECTè tornato, quindi non sono sicuro di cosa sia speso a fare quel tempo extra.
Martin Smith,

Risposte:


75

Per quanto ne so non esiste un limite massimo nel 2008.

In SQL Server 2005 il codice nella domanda non riesce nell'assegnazione alla @GGMMsgvariabile con

Tentativo di aumentare il LOB oltre la dimensione massima consentita di 2.147.483.647 byte.

il codice seguente non riesce con

REPLICARE: la lunghezza del risultato supera il limite di lunghezza (2 GB) del tipo grande di destinazione.

Tuttavia sembra che queste limitazioni siano state tranquillamente rimosse. Nel 2008

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

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

SELECT LEN(@y) 

ritorna

8589767761

L'ho eseguito sulla mia macchina desktop a 32 bit, quindi questa stringa da 8 GB è molto in eccesso rispetto alla memoria indirizzabile

In esecuzione

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

Restituito

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

quindi presumo che tutto questo venga memorizzato in LOBpagine tempdbsenza convalida sulla lunghezza. La crescita del conteggio delle pagine era tutta associata alla SET @y = REPLICATE(@y,92681);dichiarazione. L'assegnazione iniziale della variabile a @ye il LENcalcolo non hanno aumentato questo valore.

Il motivo per menzionare questo è perché il conteggio delle pagine è enormemente più di quanto mi aspettassi. Supponendo una pagina da 8 KB, allora questo funziona a 16,36 GB, che è ovviamente più o meno il doppio di quanto sembrerebbe necessario. Suppongo che ciò sia probabilmente dovuto all'inefficienza dell'operazione di concatenazione di stringhe che richiede di copiare l'intera stringa enorme e aggiungere un pezzo alla fine piuttosto che essere in grado di aggiungere alla fine della stringa esistente. Purtroppo al momento il .WRITEmetodo non è supportato per le variabili varchar (max).

Aggiunta

Ho anche testato il comportamento con la concatenazione nvarchar(max) + nvarchar(max)e nvarchar(max) + varchar(max). Entrambi consentono di superare il limite di 2 GB. Il tentativo di memorizzare i risultati di ciò in una tabella, tuttavia, fallisce Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.nuovamente con il messaggio di errore . Lo script per questo è di seguito (potrebbe richiedere molto tempo per essere eseguito).

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
Eccellente - quindi sembrerebbe che la documentazione sia piuttosto "incompleta" - noto che la pagina usuale fa riferimento a una "dimensione di archiviazione" massima, che presumibilmente si applica solo alle colonne, non alle variabili.
Damien_The_Unbeliever

@Damien - Sicuramente sembra così. Non sono sicuro che ci sia qualche altro limite che può essere raggiunto in termini di numero totale di pagine, ma penso che questo sia memorizzato in una struttura ad albero B (basata su p.381 degli interni di SQL Server 2008) quindi in linea di principio potrebbe essere esteso definitivamente.
Martin Smith,

@Damien_The_Unbeliever - La documentazione qui sembra completamente sbagliata sulla base degli esperimenti qui, affermando in modo abbastanza inequivocabile che "Variabili e parametri del tipo di dati Large Object (LOB) ... i tipi possono avere dimensioni fino a 2 GB"
Martin Smith

Un po 'inutile ma interessante però. In teoria potresti riempire il disco con una variabile ... :-)
gbn

Ho qualche esitazione qui perché memorizzare un valore in una variabile non è la stessa cosa che memorizzarlo in una colonna. Hai invece voglia di provarlo con una colonna o hai un aggiornamento? Anche SQL Server 2000 potrebbe avere varcharvalori più lunghi di 8000 caratteri in stringhe letterali nel codice, a condizione che non si sia tentato di inserirlo in una variabile o in una varcharcolonna.
ErikE

9

EDIT : Dopo ulteriori indagini, la mia ipotesi originale che questa fosse un'anomalia (bug?) Della declare @var datatype = valuesintassi non è corretta.

Ho modificato il tuo script per il 2005 poiché quella sintassi non è supportata, quindi ho provato la versione modificata nel 2008. Nel 2005, ottengo il Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.messaggio di errore. Nel 2008, lo script modificato ha ancora successo.

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)

Lo script produce sempre un errore (provando a eseguire l'inserimento della tabella), ma nel 2008 ottengo sempre un risultato nel primo set di risultati, indicando che la variabile esiste ed è più lunga di 2 ^ 31-1.
Damien_The_Unbeliever

@Damien_The_Unbeliever: ho ridotto lo script solo alla parte variabile e ora ottengo i tuoi stessi risultati. Nel 2005 ricevo l' Attempting to grow...errore sulla set @GGMMsg=...dichiarazione. Nel 2008, la sceneggiatura ha successo.
Joe Stefanelli
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.