Ukuran maksimum variabel varchar (max)

89

Pada setiap waktu di masa lalu, jika seseorang telah meminta saya ukuran maksimum untuk varchar(max), saya akan mengatakan 2GB, atau mendongak lebih tepat angka (2 ^ 31-1, atau 2147483647).

Namun, dalam beberapa pengujian baru-baru ini, saya menemukan bahwa varchar(max)variabel ternyata dapat melebihi ukuran ini:

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

Hasil:

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

Jadi, mengingat sekarang saya tahu bahwa variabel dapat melebihi penghalang 2GB - apakah ada yang tahu berapa batas sebenarnya untuk varchar(max)variabel?


(Tes di atas diselesaikan di SQL Server 2008 (bukan R2). Saya tertarik untuk mengetahui apakah ini berlaku untuk versi lain)

Damien_The_Tidak percaya
sumber
declare @x varchar(max) = 'XX'; SELECT LEN(REPLICATE(@x,2147483647))memberi 4294967294untuk saya tetapi membutuhkan waktu lama untuk menjalankan - bahkan setelah SELECTtelah kembali jadi tidak yakin apa waktu ekstra yang dihabiskan untuk melakukannya.
Martin Smith

Jawaban:

74

Sejauh yang saya tahu tidak ada batas atas di tahun 2008.

Di SQL Server 2005, kode di pertanyaan Anda gagal pada tugas ke @GGMMsgvariabel dengan

Mencoba menumbuhkan LOB melebihi ukuran maksimum yang diizinkan yaitu 2.147.483.647 byte.

kode di bawah ini gagal dengan

REPLICATE: Panjang hasil melebihi batas panjang (2GB) dari tipe target besar.

Namun tampaknya batasan ini diam-diam telah dicabut. Pada 2008

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

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

SELECT LEN(@y) 

Kembali

8589767761

Saya menjalankan ini di mesin desktop 32 bit saya sehingga string 8GB ini jauh melebihi memori yang dapat dialamatkan

Lari

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

Dikembalikan

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

jadi saya menganggap ini semua hanya disimpan di LOBhalaman tempdbtanpa validasi panjangnya. Pertumbuhan jumlah halaman semuanya terkait dengan SET @y = REPLICATE(@y,92681);pernyataan itu. Penugasan variabel awal @ydan LENpenghitungan tidak meningkatkan ini.

Alasan untuk menyebutkan ini adalah karena jumlah halaman jauh lebih banyak dari yang saya harapkan. Dengan asumsi halaman 8KB maka ini bekerja pada 16,36 GB yang jelas lebih atau kurang dua kali lipat dari apa yang tampaknya diperlukan. Saya berspekulasi bahwa ini kemungkinan karena inefisiensi dari operasi penggabungan string yang perlu menyalin seluruh string besar dan menambahkan potongan ke bagian akhir daripada dapat menambahkan ke akhir string yang ada. Sayangnya saat ini .WRITEmetode tersebut tidak didukung untuk variabel varchar (max).

Tambahan

Saya juga telah menguji perilaku tersebut dengan penggabungan nvarchar(max) + nvarchar(max)dan nvarchar(max) + varchar(max). Keduanya memungkinkan terlampaui batas 2GB. Mencoba untuk kemudian menyimpan hasil ini dalam tabel kemudian gagal namun dengan pesan kesalahan Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.lagi. Script untuk itu ada di bawah (mungkin butuh waktu lama untuk dijalankan).

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
sumber
1
Luar biasa - sehingga akan tampak bahwa dokumentasinya agak "tidak lengkap" - Saya perhatikan bahwa halaman biasa mengacu pada "ukuran penyimpanan" maksimum, yang mungkin hanya berlaku untuk kolom, bukan variabel.
Damien_The_Unbeliever
@Damien - Jelas terlihat seperti itu. Tidak yakin apakah ada batasan lain yang dapat dicapai dalam hal jumlah halaman tetapi saya pikir ini disimpan dalam struktur pohon B (berdasarkan p.381 internal SQL Server 2008) sehingga pada prinsipnya dapat diperpanjang dengan pasti.
Martin Smith
@Damien_The_Unbeliever - Dokumentasi di sini tampaknya benar-benar salah berdasarkan eksperimen di sini, yang menyatakan dengan cukup jelas bahwa "Variabel dan parameter tipe data objek besar (LOB) ... jenis dapat berukuran hingga 2 GB"
Martin Smith
Agak tidak berguna tapi menarik. Secara teori Anda dapat mengisi disk dengan satu variabel ... :-)
gbn
Saya ragu-ragu di sini karena menyimpan nilai dalam variabel tidak sama dengan menyimpannya di kolom. Apakah Anda ingin mencoba ini dengan kolom - atau apakah Anda memiliki pembaruan? Bahkan SQL Server 2000 dapat memiliki varcharnilai lebih dari 8000 karakter dalam string literal dalam kode, selama Anda tidak mencoba memasukkannya ke dalam variabel atau varcharkolom.
ErikE
9

EDIT : Setelah penyelidikan lebih lanjut, asumsi awal saya bahwa ini adalah anomali (bug?) declare @var datatype = valueSintaks tidak benar.

Saya memodifikasi skrip Anda untuk tahun 2005 karena sintaks tersebut tidak didukung, kemudian mencoba versi modifikasi pada tahun 2008. Pada tahun 2005, saya mendapatkan Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.pesan kesalahan. Pada 2008, skrip yang dimodifikasi masih berhasil.

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
sumber
Skrip selalu menghasilkan kesalahan (dengan mencoba melakukan penyisipan tabel), tetapi pada tahun 2008, saya selalu mendapatkan satu hasil di set hasil pertama, menunjukkan bahwa variabel memang ada, dan panjangnya lebih dari 2 ^ 31-1.
Damien_The_Unbeliever
@Damien_The_Unbeliever: Saya memotong skrip menjadi hanya bagian variabel dan sekarang mendapatkan hasil yang sama seperti Anda. Pada tahun 2005, saya mendapatkan Attempting to grow...error pada set @GGMMsg=...pernyataan tersebut. Pada tahun 2008, skrip tersebut berhasil.
Joe Stefanelli