SQL NVARCHAR ve VARCHAR Limitleri


100

Hepsi, büyük (kaçınılmaz) dinamik bir SQL sorgum var. Seçim kriterlerindeki alanların sayısı nedeniyle dinamik SQL içeren dizi 4000 karakterin üzerinde büyüyor. Şimdi, 4000 maks. Set olduğunu anlıyorum NVARCHAR(MAX), ancak ifade için Sunucu Profilcisi'nde yürütülen SQL'e bakıyorum

DELARE @SQL NVARCHAR(MAX);
SET @SQL = 'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO

Çalışıyor gibi görünüyor (!?), yine büyük olan başka bir sorgu için bu 4000 sınırla (!?) ilişkili bir hata atıyor, temelde bu 4000 sınırından sonra tüm SQL'i kesiyor ve bana bir sözdizimi hatası bırakıyor. Profilleyicide buna rağmen, bu dinamik SQL sorgusunu tam olarak gösteriyor (!?).

Burada tam olarak ne oluyor ve bu @SQL değişkenini VARCHAR'a dönüştürüp onunla devam etmeli miyim?

Zaman ayırdığınız için teşekkürler.

Ps. Bu büyük sorgulara bakmak için 4000'den fazla karakter yazdırabilmek de güzel olurdu. Aşağıdakiler 4000 ile sınırlıdır

SELECT CONVERT(XML, @SQL);
PRINT(@SQL);

başka güzel bir yol var mı?


3
MAX, 4000 limitinin eşanlamlısı değildir, 1..4000 veya MAX
Alex K.

Neden soruyu C # dll ile etiketlediniz ve bu sadece bir Sql Sunucusu sorusuyken sw ayarını yaptınız
HatSoft

Düzenlendi.
Gördüğünüz

PRINT 4000 karakterde (unicode için) veya 8000 karakterde (tek baytlı kodlamalar için) birleştirecektir. Sanırım buradaki karışıklığın kaynağı bu.
redcalx

Yanıtlar:


235

Maksimum 4000 ayar olduğunu anlıyorum NVARCHAR(MAX)

Anlayışınız yanlış. nvarchar(max)2 GB'a kadar (bazen daha fazla) veri (1 milyar çift bayt karakter) depolayabilir.

Gönderen nchar ve nvarchar Books online gramer olduğu

nvarchar [ ( n | max ) ]

|Karakter araçlarının bu alternatiflerdir. yani ya n ya da değişmezi belirtirsiniz max.

Spesifik bir belirlemeyi seçerseniz, nbu 1 ile 4.000 arasında olmalıdır, ancak kullanılması maxonu büyük bir nesne veri türü olarak tanımlar (yerine geçmesi ntextkullanımdan kaldırılmıştır).

Aslında SQL Server 2008'de bir değişken için 2GB sınırının süresiz olarak aşılabileceği ve tempdb( Burada gösterilmektedir )

Sorunuzun diğer kısımlarıyla ilgili olarak

Birleştirme sırasındaki kesme, veri türüne bağlıdır.

  1. varchar(n) + varchar(n) 8.000 karakterde kesilecek.
  2. nvarchar(n) + nvarchar(n) 4.000 karakterde kesilecek.
  3. varchar(n) + nvarchar(n)4.000 karakterde kesilecek. nvarchardaha yüksek önceliğe sahiptir, bu nedenle sonuçnvarchar(4,000)
  4. [n]varchar(max)+ [n]varchar(max)kesilmeyecek (<2GB için).
  5. varchar(max)+ varchar(n)kesilmeyecek (<2GB için) ve sonuç olarak yazılacaktır varchar(max).
  6. varchar(max)+ nvarchar(n)kesilmeyecek (<2GB için) ve sonuç olarak yazılacaktır nvarchar(max).
  7. nvarchar(max)+ varchar(n), önce varchar(n)girdiyi dönüştürür nvarchar(n)ve sonra birleştirmeyi yapar. Dizenin uzunluğu varchar(n)4.000 karakterden fazlaysa, atama yapılır nvarchar(4000)ve kesme gerçekleşir .

Dize değişmezlerinin veri türleri

NÖneki kullanırsanız ve dize <= 4.000 karakter uzunluğundaysa, dizenin uzunluğu nvarchar(n)nereye yazılır n. Yani örneğin N'Foo'olarak ele alınacaktır nvarchar(3). Dize 4.000 karakterden uzunsa, şu şekilde değerlendirilecektir:nvarchar(max)

NÖneki kullanmazsanız ve dize <= 8.000 karakter uzunluğundaysa, dizenin uzunluğu varchar(n)nereye yazılır n. Kadar uzunsavarchar(max)

Yukarıdakilerin her ikisi için, dizenin uzunluğu sıfırsa n, 1'e ayarlanır.

Daha yeni sözdizimi öğeleri.

1.CONCAT buradaki görevi yardımcı olmuyor

DECLARE @A5000 VARCHAR(5000) = REPLICATE('A',5000);

SELECT DATALENGTH(@A5000 + @A5000), 
       DATALENGTH(CONCAT(@A5000,@A5000));

Yukarıdaki, her iki birleştirme yöntemi için 8000 döndürür.

2. Dikkatli olun+=

DECLARE @A VARCHAR(MAX) = '';

SET @A+= REPLICATE('A',5000) + REPLICATE('A',5000)

DECLARE @B VARCHAR(MAX) = '';

SET @B = @B + REPLICATE('A',5000) + REPLICATE('A',5000)


SELECT DATALENGTH(@A), 
       DATALENGTH(@B);`

İadeler

-------------------- --------------------
8000                 10000

Not @Akarşılaştı kesme.

Yaşadığınız sorunu nasıl çözebilirsiniz.

Ya iki maxveri türünü birbirine bağladığınız için ya da bir varchar(4001 - 8000)dizeyi nvarcharyazılan bir dizeye (çift nvarchar(max)) birleştirdiğiniz için kesilme alıyorsunuz .

İkinci sorunu önlemek için, tüm dizgi değişmezlerinin (veya en azından 4001 - 8000 aralığında uzunluklara sahip olanların) önünde olduğundan emin olun N.

İlk sorunu önlemek için atamayı

DECLARE @SQL NVARCHAR(MAX);
SET @SQL = 'Foo' + 'Bar' + ...;

İçin

DECLARE @SQL NVARCHAR(MAX) = ''; 
SET @SQL = @SQL + N'Foo' + N'Bar'

böylece bir NVARCHAR(MAX)birleştirmeye baştan dahil edilir (her birleştirme sonucunda NVARCHAR(MAX)bu da yayılır)

Görüntülerken kesilmekten kaçınmak

"Kılavuza göre sonuçlar" modunun seçili olduğundan emin olun, ardından kullanabilirsiniz

select @SQL as [processing-instruction(x)] FOR XML PATH 

SSMS seçenekleri, XMLsonuçlar için sınırsız uzunluk ayarlamanıza izin verir . processing-instructionBiraz gibi karakterlerle ilgili sorunları önler <olarak gösterilmesini &lt;.


2
@Killercam - nvarchar(4000)Yol boyunca örtük bir oyuncu kadrosu alıyor olabilirsiniz . Bir dize değişmezi 4.000 karakterden azsa, olarak değerlendirilir nvarchar(x). Onu başka bir nvarchar(x)değerle birleştirmek, yukarı doğru tahmin etmek yerine kesileceknvarchar(max)
Martin Smith

2
@Killercam - Muhtemelen ilk yorumuma göre kesiliyorsunuz. Birleştirmeye DECLARE @SQL NVARCHAR(MAX) = ''; SET @SQL = @SQL + bir NVARCHAR(MAX)dahil edilecek şekilde atamayı değiştirmeyi deneyin .
Martin Smith

2
@Killercam - Muhtemelen 4.000 ile 8.000 karakter arasında bir dizeniz vardır. İle Nolarak ele alınacaktır önek nvarchar(max)o kadar işlem görecektir onsuz varchar(n)sonra örtülü olarak artığını nvarchar(4000)Bir üzere bitiştirmeknvarchar
Martin Smith

3
Bu cevapla aydınlandım
Müdassir Hasan

1
Harika cevap. Çok teşekkürler!
John Bell

6

Tamam, eğer daha sonra satırın sonunda sorun, izin verilen boyuttan daha büyük bir sorgunuz olması ise (bu, büyümeye devam ederse gerçekleşebilir), onu parçalara ayırmanız ve dize değerlerini çalıştırmanız gerekecek. Öyleyse, aşağıdaki gibi bir saklı yordamınız olduğunu varsayalım:

CREATE PROCEDURE ExecuteMyHugeQuery
    @SQL VARCHAR(MAX) -- 2GB size limit as stated by Martin Smith
AS
BEGIN
    -- Now, if the length is greater than some arbitrary value
    -- Let's say 2000 for this example
    -- Let's chunk it
    -- Let's also assume we won't allow anything larger than 8000 total
    DECLARE @len INT
    SELECT @len = LEN(@SQL)

    IF (@len > 8000)
    BEGIN
        RAISERROR ('The query cannot be larger than 8000 characters total.',
                   16,
                   1);
    END

    -- Let's declare our possible chunks
    DECLARE @Chunk1 VARCHAR(2000),
            @Chunk2 VARCHAR(2000),
            @Chunk3 VARCHAR(2000),
            @Chunk4 VARCHAR(2000)

    SELECT @Chunk1 = '',
           @Chunk2 = '',
           @Chunk3 = '',
           @Chunk4 = ''

    IF (@len > 2000)
    BEGIN
        -- Let's set the right chunks
        -- We already know we need two chunks so let's set the first
        SELECT @Chunk1 = SUBSTRING(@SQL, 1, 2000)

        -- Let's see if we need three chunks
        IF (@len > 4000)
        BEGIN
            SELECT @Chunk2 = SUBSTRING(@SQL, 2001, 2000)

            -- Let's see if we need four chunks
            IF (@len > 6000)
            BEGIN
                SELECT @Chunk3 = SUBSTRING(@SQL, 4001, 2000)
                SELECT @Chunk4 = SUBSTRING(@SQL, 6001, (@len - 6001))
            END
              ELSE
            BEGIN
                SELECT @Chunk3 = SUBSTRING(@SQL, 4001, (@len - 4001))
            END
        END
          ELSE
        BEGIN
            SELECT @Chunk2 = SUBSTRING(@SQL, 2001, (@len - 2001))
        END
    END

    -- Alright, now that we've broken it down, let's execute it
    EXEC (@Chunk1 + @Chunk2 + @Chunk3 + @Chunk4)
END

2

Sen de nvarchar metnini kullanmalısın. Bu, büyük dizginizden önce basitçe bir "N" olması gerektiği anlamına gelir ve işte bu kadar! artık sınırlama yok

DELARE @SQL NVARCHAR(MAX);
SET @SQL = N'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO

3
Bu resmin tamamı değil ... Eğer N önekini kullanırsanız ve dize <= 4.000 karakter nvarchar(n)uzunluğundaysa, n, dizenin uzunluğu olarak yazılacaktır . Yani N'Foo ' nvarchar(3)örneğin kabul edilecektir . Dize 4.000 karakterden uzunsa, olarak değerlendirilecektir nvarchar(max). N önekini kullanmazsanız ve dize <= 8.000 karakter varchar(n)uzunluğundaysa, n, dizenin uzunluğu olacak şekilde yazılacaktır . Eğer daha uzunsa varchar(max). Yukarıdakilerin her ikisi için de dizenin uzunluğu sıfırsa n değeri 1'e ayarlanır.
MoonKnight

1

Kabul edilen cevap bana yardımcı oldu, ancak vaka ifadelerini içeren değişkenleri bir araya getirirken ayağa kalktım. OP'nin sorusunun vaka ifadeleri içermediğini biliyorum, ancak vaka ifadelerini içeren uzun dinamik SQL ifadeleri oluşturmak için uğraşırken burada sona eren benim gibi başkaları için buraya göndermenin faydalı olacağını düşündüm.

Dize birleştirme ile case ifadeleri kullanıldığında, kabul edilen cevapta belirtilen kurallar, case ifadesinin her bölümü için bağımsız olarak geçerlidir.

declare @l_sql varchar(max) = ''

set @l_sql = @l_sql +
case when 1=1 then
    --without this correction the result is truncated
    --CONVERT(VARCHAR(MAX), '')
 +REPLICATE('1', 8000)
 +REPLICATE('1', 8000)
end

print len(@l_sql)

0
declare @p varbinary(max)
set @p = 0x
declare @local table (col text)

SELECT   @p = @p + 0x3B + CONVERT(varbinary(100), Email)
 FROM tbCarsList
 where email <> ''
 group by email
 order by email

 set @p = substring(@p, 2, 100000)

 insert @local values(cast(@p as varchar(max)))
 select DATALENGTH(col) as collen, col from @local

result collen > 8000, length col value is more than 8000 chars
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.