Bir varchar (max) değişkeninin maksimum boyutu


90

Geçmişte herhangi bir zamanda, birisi bana a için maksimum boyutu sormuş varchar(max)olsaydı, 2GB derdim veya daha kesin bir rakama bakardım (2 ^ 31-1 veya 2147483647).

Bununla birlikte, son zamanlarda yapılan bazı testlerde, varchar(max)değişkenlerin görünüşte bu boyutu aşabileceğini keşfettim :

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

Sonuçlar:

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

Öyleyse, artık bir değişkenin 2GB bariyerini aşabileceğini bildiğime göre - bir değişken için gerçek sınırın ne olduğunu bilen var varchar(max)mı?


(Yukarıdaki test SQL Server 2008'de tamamlandı (R2 değil). Diğer sürümler için geçerli olup olmadığını bilmek isterim)


declare @x varchar(max) = 'XX'; SELECT LEN(REPLICATE(@x,2147483647))4294967294bana veriyor ama koşması uzun zaman alıyor - SELECTgeri döndükten sonra bile, bu yüzden fazladan ne kadar zaman harcandığından emin değilim.
Martin Smith

Yanıtlar:


75

Anladığım kadarıyla 2008'de üst sınır yok.

SQL Server 2005'te sorunuzdaki kod, @GGMMsgdeğişkene atamada başarısız oluyor.

LOB'yi izin verilen maksimum boyut olan 2.147.483.647 bayt'ın üzerine çıkarmaya çalışılıyor.

aşağıdaki kod ile başarısız olur

TEKRARLA: Sonucun uzunluğu, hedef büyük türün uzunluk sınırını (2 GB) aşıyor.

Ancak görünen o ki bu sınırlamalar sessizce kaldırılmış. 2008'de

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

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

SELECT LEN(@y) 

İadeler

8589767761

Bunu 32 bit masaüstü makinemde çalıştırdım, bu nedenle bu 8GB dize adreslenebilir belleği aşıyor

Koşu

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

İade

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

bu yüzden tüm bunların uzunluk doğrulaması yapılmadan LOBsayfalarda saklandığını varsayıyorum tempdb. Sayfa sayısındaki artış, SET @y = REPLICATE(@y,92681);ifadeyle ilişkilendirildi . İlk değişken ataması @yve LENhesaplama bunu artırmadı.

Bundan bahsetmenin nedeni, sayfa sayısının beklediğimden çok daha fazla olmasıdır. 8KB'lik bir sayfa varsayarsak, bu 16.36 GB'de çalışır ve bu da gerekli görünenin neredeyse iki katıdır. Bunun büyük olasılıkla mevcut dizginin sonuna eklemek yerine tüm büyük dizgiyi kopyalayıp sonuna bir yığın eklemeye ihtiyaç duyan dizi birleştirme işleminin verimsizliğinden kaynaklandığını tahmin ediyorum. Maalesef şu anda .WRITEyöntem varchar (max) değişkenleri için desteklenmiyor .

İlave

Davranışı birleştirme nvarchar(max) + nvarchar(max)ve ile de test ettim nvarchar(max) + varchar(max). Bunların her ikisi de 2GB sınırının aşılmasına izin verir. Bunun sonuçlarını bir tabloya kaydetmeye çalışmak daha sonra hata mesajıyla Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.tekrar başarısız olur . Bunun için komut dosyası aşağıdadır (çalıştırılması uzun sürebilir).

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
Mükemmel - bu nedenle dokümantasyonun daha çok "eksik" olduğu anlaşılıyor - Olağan sayfanın, değişkenler için değil, muhtemelen yalnızca sütunlar için geçerli olan maksimum bir "depolama boyutuna" atıfta bulunduğunu not ediyorum.
Damien_The_Unbeliever

@Damien - Kesinlikle bu şekilde görünüyor. Toplam sayfa sayısı açısından ulaşılabilecek başka bir sınır olup olmadığından emin değilim, ancak bunun bir B ağacı yapısında saklandığını düşünüyorum (SQL Server 2008 iç kısımlarının 381'ine dayanarak), bu nedenle prensipte kesinlikle genişletilebilir.
Martin Smith

@Damien_The_Unbeliever - Buradaki dokümantasyon , buradaki deneylere dayanarak açıkça yanlış görünüyor ve oldukça açık bir şekilde "Büyük nesne (LOB) veri türü değişkenleri ve parametreleri ... türlerin boyut olarak 2 GB'a kadar olabileceğini" belirtiyor
Martin Smith

Gerçi işe yaramaz ama ilginç. Teorik olarak diski tek bir değişkenle doldurabilirsiniz ... :-)
gbn

Burada biraz tereddütüm var çünkü bir değişkende bir değer depolamak, onu bir sütunda saklamakla aynı şey değildir. Bunu bir sütunla denemek ister misiniz - yoksa bir güncellemeniz mi var? SQL Server 2000 bile, varcharonu bir değişkene veya varcharsütuna koymaya çalışmadığınız sürece, koddaki değişmez dizelerde 8000 karakterden uzun değerlere sahip olabilir .
ErikE

9

DÜZENLEME : Daha fazla araştırmadan sonra, bunun declare @var datatype = valuesözdiziminin bir anormalliği (hata?) Olduğuna dair orijinal varsayım yanlış.

Bu sözdizimi desteklenmediği için betiğinizi 2005 için değiştirdim, ardından 2008'de değiştirilmiş sürümü denedim. 2005'te Attempting to grow LOB beyond maximum allowed size of 2147483647 bytes.hata mesajını alıyorum. 2008 yılında, değiştirilmiş komut dosyası hala başarılıdır.

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)

Komut dosyası her zaman bir hata üretir (tablo eklemeye çalışarak), ancak 2008'de her zaman ilk sonuç kümesinde değişkenin var olduğunu ve 2 ^ 31-1 uzunluğundan daha uzun olduğunu belirten bir sonuç alırım.
Damien_The_Unbeliever

@Damien_The_Unbeliever: Senaryoyu sadece değişken kısmına indirdim ve şimdi seninle aynı sonuçları alıyorum. 2005 Attempting to grow...yılında set @GGMMsg=...ifadede hatayı alıyorum . 2008'de senaryo başarılı oldu.
Joe Stefanelli
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.