Batas NVARCHAR dan VARCHAR SQL

100

Semua, saya memiliki kueri SQL dinamis yang besar (tidak dapat dihindari). Karena jumlah bidang dalam kriteria pemilihan, string yang berisi SQL dinamis bertambah lebih dari 4000 karakter. Sekarang, saya mengerti bahwa ada 4000 max set NVARCHAR(MAX), tapi melihat SQL yang dieksekusi di Server Profiler untuk pernyataan itu

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

Tampaknya berfungsi (!?), untuk kueri lain yang juga besar itu memunculkan kesalahan yang terkait dengan batas 4000 ini (!?), itu pada dasarnya memangkas semua SQL setelah batas 4000 ini dan meninggalkan saya dengan kesalahan sintaksis. Meskipun demikian di profiler, ini menunjukkan kueri SQL dinamis ini secara penuh (!?).

Apa sebenarnya yang terjadi di sini dan haruskah saya mengonversi variabel @SQL ini ke VARCHAR dan melanjutkannya?

Terima kasih atas waktunya.

Ps. Akan menyenangkan juga bisa mencetak lebih dari 4000 karakter untuk melihat kueri besar ini. Berikut ini dibatasi hingga 4000

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

apakah ada cara keren lainnya?

Ksatria bulan
sumber
3
MAX bukan sinonim untuk batas 4000, itu 1..4000 atau MAX
Alex K.
Mengapa Anda menandai pertanyaan dengan C # dll & pengaturan padahal ini hanya pertanyaan Sql Server
HatSoft
Diedit. Terima kasih sudah melihat ...
MoonKnight
PRINT akan digabungkan pada 4000 karakter (untuk unicode) atau 8000 karakter (untuk pengkodean byte tunggal). Saya menduga itulah sumber kebingungan di sini.
redcalx

Jawaban:

235

Saya memahami bahwa ada set maks 4000 untuk NVARCHAR(MAX)

Pemahaman Anda salah. nvarchar(max)dapat menyimpan hingga (dan terkadang lebih dari itu) 2GB data (1 miliar karakter byte ganda).

Dari nchar dan nvarchar di Buku online tata bahasanya adalah

nvarchar [ ( n | max ) ]

The |karakter cara ini alternatif. yaitu Anda menentukan salah satu n atau literal max.

Jika Anda memilih untuk menentukan spesifik nmaka ini harus antara 1 dan 4.000 tetapi menggunakan maxmendefinisikannya sebagai tipe data objek besar (penggantinya ntexttidak digunakan lagi).

Faktanya di SQL Server 2008 tampaknya untuk variabel batas 2GB dapat dilampaui tanpa batas waktu tergantung pada ruang yang cukup di tempdb( Ditunjukkan di sini )

Mengenai bagian lain dari pertanyaan Anda

Pemotongan saat penggabungan bergantung pada tipe data.

  1. varchar(n) + varchar(n) akan dipotong menjadi 8.000 karakter.
  2. nvarchar(n) + nvarchar(n) akan dipotong menjadi 4.000 karakter.
  3. varchar(n) + nvarchar(n)akan dipotong menjadi 4.000 karakter. nvarcharmemiliki prioritas yang lebih tinggi sehingga hasilnya adalahnvarchar(4,000)
  4. [n]varchar(max)+ [n]varchar(max)tidak akan dipotong (untuk <2GB).
  5. varchar(max)+ varchar(n)tidak akan dipotong (untuk <2GB) dan hasilnya akan diketik sebagai varchar(max).
  6. varchar(max)+ nvarchar(n)tidak akan dipotong (untuk <2GB) dan hasilnya akan diketik sebagai nvarchar(max).
  7. nvarchar(max)+ varchar(n)pertama-tama akan mengubah varchar(n)input menjadi nvarchar(n)dan kemudian melakukan penggabungan. Jika panjang varchar(n)string lebih dari 4.000 karakter maka cast akan menjadi nvarchar(4000)dan pemotongan akan terjadi .

Jenis data literal string

Jika menggunakan Nawalan dan panjang string <= 4.000 karakter maka akan diketik nvarchar(n)dimana npanjang stringnya. Jadi N'Foo'akan diperlakukan sebagai nvarchar(3)contoh. Jika string lebih dari 4.000 karakter itu akan diperlakukan sebagainvarchar(max)

Jika Anda tidak menggunakan Nawalan dan panjang string <= 8.000 karakter maka akan diketik varchar(n)dimana npanjang stringnya. Jika lebih lamavarchar(max)

Untuk kedua hal di atas jika panjang string adalah nol maka ndiset ke 1.

Elemen sintaks yang lebih baru.

1. The CONCATFungsi tidak membantu di sini

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

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

Di atas mengembalikan 8000 untuk kedua metode penggabungan.

2. Hati-hati dengan+=

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);`

Kembali

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

Perhatikan bahwa @Amengalami pemotongan.

Cara mengatasi masalah yang Anda alami.

Anda mendapatkan pemotongan baik karena Anda menggabungkan dua maxtipe data non bersama-sama atau karena Anda menggabungkan varchar(4001 - 8000)string ke string yang nvarchardiketik (genap nvarchar(max)).

Untuk menghindari masalah kedua cukup pastikan bahwa semua literal string (atau setidaknya yang memiliki panjang dalam kisaran 4001 - 8000) diawali dengan N.

Untuk menghindari masalah pertama, ubah tugas dari

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

Untuk

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

sehingga sebuah NVARCHAR(MAX)terlibat dalam penggabungan dari awal (sebagai hasil dari setiap penggabungan juga NVARCHAR(MAX)akan menyebar)

Menghindari pemotongan saat melihat

Pastikan Anda memilih mode "hasil ke kisi", lalu Anda dapat menggunakan

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

Opsi SSMS memungkinkan Anda menyetel panjang tak terbatas untuk XMLhasil. The processing-instructionbit menghindari masalah dengan karakter seperti <muncul sebagai &lt;.

Martin Smith
sumber
2
@Kamam - Anda mungkin mendapatkan pemeran implisit di nvarchar(4000)sepanjang jalan. Jika literal string kurang dari 4.000 karakter maka itu akan diperlakukan sebagai nvarchar(x). nvarchar(x)Menggabungkannya nilai lain akan dipotong alih-alih dialihkan kenvarchar(max)
Martin Smith
2
@Kam - Anda mungkin mendapatkan pemotongan sesuai komentar pertama saya. Coba ubah tugas menjadi DECLARE @SQL NVARCHAR(MAX) = ''; SET @SQL = @SQL + sehingga NVARCHAR(MAX)terlibat dalam penggabungan.
Martin Smith
2
@Kam - Mungkin Anda memiliki string antara 4.000 dan 8.000 karakter. Dengan Nawalan yang akan diperlakukan sebagai nvarchar(max)tanpanya, itu akan diperlakukan sebagai varchar(n)kemudian secara implisit dilemparkan ke nvarchar(4000)saat Anda menggabungkan kenvarchar
Martin Smith
3
Saya tercerahkan oleh jawaban ini
Mudassir Hasan
1
Jawaban yang luar biasa. Terima kasih banyak!
John Bell
6

Oke, jadi jika nanti masalahnya adalah Anda memiliki kueri yang lebih besar dari ukuran yang diizinkan (yang mungkin terjadi jika terus bertambah) Anda harus memecahnya menjadi beberapa bagian dan mengeksekusi nilai string. Jadi, katakanlah Anda memiliki prosedur tersimpan seperti berikut:

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
Mike Perrenoud
sumber
2

Anda juga harus menggunakan teks nvarchar. itu berarti Anda hanya perlu memiliki "N" sebelum string besar Anda dan hanya itu! tidak ada batasan lagi

DELARE @SQL NVARCHAR(MAX);
SET @SQL = N'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO
Max
sumber
3
Ini bukan keseluruhan gambar ... Jika Anda menggunakan awalan N dan panjang string <= 4.000 karakter maka akan diketik nvarchar(n)dimana n adalah panjang string. Jadi N'Foo 'akan diperlakukan sebagai nvarchar(3)contoh. Jika string lebih dari 4.000 karakter itu akan diperlakukan sebagai nvarchar(max). Jika Anda tidak menggunakan awalan N dan panjang string <= 8.000 karakter maka akan diketik varchar(n)dimana n adalah panjang string. Jika lebih lama varchar(max). Untuk kedua hal di atas jika panjang string adalah nol maka n diatur ke 1.
MoonKnight
1

Jawaban yang diterima membantu saya tetapi saya tersandung saat melakukan rangkaian varchar yang melibatkan pernyataan kasus. Saya tahu pertanyaan OP tidak melibatkan pernyataan kasus tetapi saya pikir ini akan membantu untuk memposting di sini untuk orang lain seperti saya yang berakhir di sini sambil berjuang untuk membangun pernyataan SQL dinamis yang panjang yang melibatkan pernyataan kasus.

Saat menggunakan pernyataan kasus dengan rangkaian string, aturan yang disebutkan dalam jawaban yang diterima berlaku untuk setiap bagian pernyataan kasus secara independen.

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)
Joe
sumber
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
Heta77
sumber