SQL NVARCHAR และ VARCHAR Limits


100

ทั้งหมดฉันมีแบบสอบถาม SQL แบบไดนามิกขนาดใหญ่ (หลีกเลี่ยงไม่ได้) เนื่องจากจำนวนฟิลด์ในเกณฑ์การเลือกสตริงที่มีไดนามิก SQL จึงเพิ่มขึ้นมากกว่า 4000 ตัวอักษร ตอนนี้ฉันเข้าใจว่ามีชุดสูงสุด 4000 สำหรับNVARCHAR(MAX)แต่ดูที่ SQL ที่เรียกใช้งานใน Server Profiler สำหรับคำสั่ง

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

ดูเหมือนว่าจะใช้งานได้ (!?) สำหรับแบบสอบถามอื่นที่มีขนาดใหญ่เช่นกันมันแสดงข้อผิดพลาดซึ่งเกี่ยวข้องกับขีด จำกัด 4000 นี้ (!?) โดยทั่วไปจะตัด SQL ทั้งหมดหลังจากขีด จำกัด 4000 นี้และทำให้ฉันมีข้อผิดพลาดทางไวยากรณ์ แม้จะมีสิ่งนี้ใน profiler แต่ก็แสดงแบบสอบถาม SQL แบบไดนามิกนี้แบบเต็ม (!?)

เกิดอะไรขึ้นที่นี่และฉันควรจะแปลงตัวแปร @SQL นี้เป็น VARCHAR และดำเนินการต่อไปหรือไม่

ขอบคุณที่สละเวลา.

Ps. นอกจากนี้ยังเป็นการดีที่สามารถพิมพ์ตัวอักษรมากกว่า 4,000 ตัวเพื่อดูข้อความค้นหาขนาดใหญ่เหล่านี้ ต่อไปนี้ จำกัด ที่ 4000

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

มีวิธีเด็ด ๆ อีกไหม?


3
MAX ไม่ใช่คำพ้องความหมายสำหรับขีด จำกัด 4000 คือ 1..4000 หรือ MAX
Alex K.

ทำไมคุณถึงติดแท็กคำถามด้วย C # dll & การตั้งค่า sw แล้วนี่เป็นเพียงคำถามเซิร์ฟเวอร์ Sql
HatSoft

แก้ไขแล้ว ขอบคุณที่บอก ...
MoonKnight

PRINT จะต่อกันที่ 4000 อักขระ (สำหรับ Unicode) หรือ 8000 ตัวอักษร (สำหรับการเข้ารหัสแบบไบต์เดียว) ฉันสงสัยว่านั่นคือที่มาของความสับสนที่นี่
redcalx

คำตอบ:


235

ฉันเข้าใจว่ามีชุดสูงสุด 4000 สำหรับ NVARCHAR(MAX)

ความเข้าใจของคุณผิด nvarchar(max)สามารถจัดเก็บข้อมูลได้มากถึง (และบางครั้ง) 2GB (1 พันล้านอักขระไบต์คู่)

จากnchar และ nvarcharในหนังสือออนไลน์ไวยากรณ์คือ

nvarchar [ ( n | max ) ]

|หมายถึงตัวละครเหล่านี้เป็นทางเลือก เช่นคุณระบุอย่างใดอย่างหนึ่ง หรือตัวอักษรnmax

หากคุณเลือกระบุเฉพาะค่าnนี้จะต้องอยู่ระหว่าง 1 ถึง 4,000 แต่การใช้maxกำหนดเป็นประเภทข้อมูลออบเจ็กต์ขนาดใหญ่ (แทนที่ntextซึ่งเลิกใช้งานแล้ว)

ในความเป็นจริงใน SQL Server 2008 ดูเหมือนว่าสำหรับตัวแปรขีด จำกัด 2GB สามารถเกินได้อย่างไม่มีกำหนดโดยมีพื้นที่ว่างเพียงพอในtempdb( แสดงที่นี่ )

เกี่ยวกับส่วนอื่น ๆ ของคำถามของคุณ

การตัดทอนเมื่อเชื่อมต่อกันขึ้นอยู่กับประเภทข้อมูล

  1. varchar(n) + varchar(n) จะตัดที่ 8,000 อักขระ
  2. nvarchar(n) + nvarchar(n) จะตัดที่ 4,000 อักขระ
  3. varchar(n) + nvarchar(n)จะตัดที่ 4,000 อักขระ nvarcharมีลำดับความสำคัญสูงกว่าดังนั้นผลลัพธ์ก็คือnvarchar(4,000)
  4. [n]varchar(max)+ [n]varchar(max)จะไม่ตัดทอน (สำหรับ <2GB)
  5. varchar(max)+ varchar(n)จะไม่ตัดทอน (สำหรับ <2GB) และผลลัพธ์จะถูกพิมพ์เป็นvarchar(max).
  6. varchar(max)+ nvarchar(n)จะไม่ตัดทอน (สำหรับ <2GB) และผลลัพธ์จะถูกพิมพ์เป็นnvarchar(max).
  7. nvarchar(max)+ varchar(n)ก่อนอื่นจะแปลงvarchar(n)อินพุตเป็นnvarchar(n)แล้วทำการต่อข้อมูล ถ้าความยาวของ varchar(n)สตริงเป็นจำนวนมากกว่า 4,000 ตัวอักษรหล่อจะได้รับการnvarchar(4000)และการตัดจะเกิดขึ้น

ประเภทข้อมูลของตัวอักษรสตริง

ถ้าคุณใช้Nคำนำหน้าและสตริงคือ <= 4,000 ตัวอักษรก็จะมีการพิมพ์เป็นnvarchar(n)ที่nมีความยาวของสตริง ดังนั้นN'Foo'จะถือว่าเป็นnvarchar(3)ตัวอย่าง หากสตริงมีความยาวเกิน 4,000 อักขระจะถือว่าเป็นnvarchar(max)

หากคุณไม่ได้ใช้Nคำนำหน้าและสตริงคือ <= 8,000 ตัวอักษรก็จะมีการพิมพ์เป็นvarchar(n)ที่nมีความยาวของสตริง ถ้านานกว่านั้นvarchar(max)

สำหรับทั้งสองข้อข้างต้นหากความยาวของสตริงเป็นศูนย์nให้ตั้งค่าเป็น 1

องค์ประกอบไวยากรณ์ที่ใหม่กว่า

1.CONCATฟังก์ชั่นไม่ได้ช่วยที่นี่

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

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

ข้างต้นจะคืนค่า 8000 สำหรับทั้งสองวิธีการต่อกัน

2.ระวังด้วย+=

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

ผลตอบแทน

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

โปรดทราบว่า@Aพบการตัดทอน

วิธีแก้ไขปัญหาที่คุณพบ

คุณได้รับการตัดทอนเนื่องจากคุณกำลังเชื่อมต่อmaxประเภทข้อมูลที่ไม่ใช่สองประเภทเข้าด้วยกันหรือเนื่องจากคุณกำลังเชื่อมvarchar(4001 - 8000)สตริงเข้ากับสตริงที่nvarcharพิมพ์ (คู่nvarchar(max))

เพื่อหลีกเลี่ยงปัญหาที่สองเพียงให้แน่ใจว่าทุกตัวอักษรของสตริง (หรืออย่างน้อยผู้ที่มีความยาวใน 4001-8000 ช่วง) Nจะนำเข้าด้วย

เพื่อหลีกเลี่ยงปัญหาแรกให้เปลี่ยนงานจาก

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

ถึง

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

เพื่อให้ an NVARCHAR(MAX)มีส่วนร่วมในการเชื่อมต่อตั้งแต่เริ่มต้น (เนื่องจากผลของการเรียงต่อกันแต่ละครั้งจะเป็นการNVARCHAR(MAX)เผยแพร่ด้วย)

หลีกเลี่ยงการตัดทอนเมื่อรับชม

ตรวจสอบให้แน่ใจว่าคุณได้เลือกโหมด "ผลลัพธ์เป็นตาราง" แล้วจึงจะสามารถใช้ได้

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

ตัวเลือก SSMS ช่วยให้คุณกำหนดความยาวได้ไม่ จำกัด สำหรับXMLผลลัพธ์ processing-instructionบิตหลีกเลี่ยงปัญหาเกี่ยวกับตัวอักษรเช่นการแสดงขึ้นเป็น<&lt;


2
@Killercam - คุณอาจจะได้รับการคัดเลือกโดยปริยายnvarchar(4000)ระหว่างทาง ถ้าตัวอักษรสตริงน้อยกว่า 4,000 nvarchar(x)ตัวอักษรแล้วก็จะถือว่าเป็น การเชื่อมต่อกับnvarchar(x)ค่าอื่นจะตัดทอนแทนที่จะอัปเดตเป็นnvarchar(max)
Martin Smith

2
@Killercam - คุณอาจจะถูกตัดทอนตามความคิดเห็นแรกของฉัน ลองเปลี่ยนการมอบหมายเพื่อDECLARE @SQL NVARCHAR(MAX) = ''; SET @SQL = @SQL + ให้มีNVARCHAR(MAX)ส่วนร่วมในการเชื่อมต่อ
Martin Smith

2
@Killercam - คุณอาจมีสตริงระหว่าง 4,000 ถึง 8,000 อักขระ ด้วยNคำนำหน้าว่าจะได้รับการปฏิบัติnvarchar(max)โดยไม่ได้ก็จะถือว่าเป็นvarchar(n)แล้วโยนโดยปริยายnvarchar(4000)เมื่อคุณเชื่อมต่อไปยังnvarchar
มาร์ตินสมิ ธ

3
ฉันรู้แจ้งในคำตอบนี้
Mudassir Hasan

1
คำตอบที่ยอดเยี่ยม ขอบคุณมาก!
John Bell

6

โอเคดังนั้นหากในภายหลังปัญหาก็คือคุณมีแบบสอบถามที่มากกว่าขนาดที่อนุญาต (ซึ่งอาจเกิดขึ้นถ้ามันเติบโตขึ้นเรื่อย ๆ ) คุณจะต้องแยกมันออกเป็นชิ้น ๆ และดำเนินการตามค่าสตริง สมมติว่าคุณมีขั้นตอนการจัดเก็บดังต่อไปนี้:

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

คุณใช้ข้อความ nvarchar ด้วย นั่นหมายความว่าคุณต้องมี "N" ก่อนสตริงขนาดใหญ่ของคุณและนั่นแหล่ะ! ไม่มีข้อ จำกัด อีกต่อไป

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

3
นี่ไม่ใช่ภาพทั้งหมด ... หากคุณใช้คำนำหน้า N และสตริงมีความยาว <= 4,000 อักขระระบบจะพิมพ์โดยnvarchar(n)ที่ n คือความยาวของสตริง ดังนั้น N'Foo 'จะได้รับการปฏิบัติnvarchar(3)เช่น ถ้าสตริงที่มีความยาวกว่า 4,000 nvarchar(max)ตัวอักษรก็จะถือว่าเป็น หากคุณไม่ใช้คำนำหน้า N และสตริงมีความยาว <= 8,000 อักขระระบบจะพิมพ์โดยvarchar(n)ที่ n คือความยาวของสตริง ถ้านานกว่าvarchar(max)นั้น สำหรับทั้งสองข้อข้างต้นหากความยาวของสตริงเป็นศูนย์ดังนั้น n จะถูกตั้งค่าเป็น 1
MoonKnight

1

คำตอบที่ได้รับการยอมรับช่วยฉันได้ แต่ฉันสะดุดในขณะที่ทำการเรียงต่อกันของ varchars ที่เกี่ยวข้องกับคำชี้แจงกรณี ฉันรู้ว่าคำถามของ OP ไม่เกี่ยวข้องกับคำสั่งกรณี แต่ฉันคิดว่าสิ่งนี้จะเป็นประโยชน์ในการโพสต์ที่นี่สำหรับคนอื่น ๆ เช่นฉันที่ลงเอยที่นี่ในขณะที่พยายามสร้างคำสั่ง SQL แบบไดนามิกที่ยาวซึ่งเกี่ยวข้องกับคำสั่ง case

เมื่อใช้คำสั่ง case กับการต่อสตริงกฎที่กล่าวถึงในคำตอบที่ยอมรับจะใช้กับแต่ละส่วนของคำสั่ง case อย่างเป็นอิสระ

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.