SQL NVARCHAR và Giới hạn VARCHAR


100

Tất cả, tôi có một truy vấn SQL động lớn (không thể tránh khỏi). Do số lượng trường trong tiêu chí lựa chọn, chuỗi chứa SQL động đang tăng hơn 4000 ký tự. Bây giờ, tôi hiểu rằng có 4000 bộ tối đa cho NVARCHAR(MAX), nhưng nhìn vào SQL được thực thi trong Trình biên dịch máy chủ để biết câu lệnh

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

Có vẻ hoạt động (!?), đối với một truy vấn khác cũng lớn, nó tạo ra một lỗi liên quan đến giới hạn 4000 này (!?), về cơ bản nó cắt bỏ tất cả SQL sau giới hạn 4000 này và để lại cho tôi một lỗi cú pháp. Mặc dù vậy trong trình biên dịch, nó đang hiển thị đầy đủ truy vấn SQL động này (!?).

Chính xác thì điều gì đang xảy ra ở đây và tôi có nên chuyển đổi biến @SQL này thành VARCHAR và tiếp tục với nó không?

Cảm ơn vì đã dành thời gian cho tôi.

Ps. Thật tuyệt nếu có thể in ra hơn 4000 ký tự để xem những truy vấn lớn này. Những thứ sau được giới hạn ở 4000

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

có cách nào khác hay không?


3
MAX không phải là từ đồng nghĩa với giới hạn 4000, 1..4000 hoặc MAX của nó
Alex K.

Tại sao bạn đã gắn thẻ câu hỏi với C # dll & đặt cài swhen đây chỉ là một câu hỏi Sql Server
HatSoft

Đã chỉnh sửa. Cám ơn đốm ...
MoonKnight

PRINT sẽ ghép ở 4000 ký tự (đối với unicode) hoặc 8000 ký tự (đối với mã hóa byte đơn). Tôi nghi ngờ rằng đó là nguồn gốc của sự nhầm lẫn ở đây.
redcalx

Câu trả lời:


235

Tôi hiểu rằng có 4000 bộ tối đa cho NVARCHAR(MAX)

Sự hiểu biết của bạn là sai. nvarchar(max)có thể lưu trữ tối đa (và đôi khi cao hơn) 2GB dữ liệu (1 tỷ ký tự byte kép).

Từ nchar và nvarchar trong Sách trực tuyến, ngữ pháp là

nvarchar [ ( n | max ) ]

|tự có nghĩa là đây là những lựa chọn thay thế. tức là bạn chỉ định một trong hai n hoặc theo nghĩa đen max.

Nếu bạn chọn chỉ định một cụ thể nthì giá trị này phải nằm trong khoảng từ 1 đến 4.000 nhưng sử dụng maxxác định nó là một kiểu dữ liệu đối tượng lớn (thay thế cho kiểu dữ liệu ntextnày không được dùng nữa).

Trên thực tế, trong SQL Server 2008, có vẻ như đối với một biến , giới hạn 2GB có thể bị vượt quá vô thời hạn tùy thuộc vào đủ dung lượng trong tempdb( Hiển thị tại đây )

Về các phần khác của câu hỏi của bạn

Việc cắt bớt khi nối phụ thuộc vào kiểu dữ liệu.

  1. varchar(n) + varchar(n) sẽ cắt ngắn ở 8.000 ký tự.
  2. nvarchar(n) + nvarchar(n) sẽ cắt bớt 4.000 ký tự.
  3. varchar(n) + nvarchar(n)sẽ cắt bớt 4.000 ký tự. nvarcharcó mức độ ưu tiên cao hơn nên kết quả lànvarchar(4,000)
  4. [n]varchar(max)+ [n]varchar(max)sẽ không cắt bớt (cho <2GB).
  5. varchar(max)+ varchar(n)sẽ không cắt bớt (đối với <2GB) và kết quả sẽ được nhập là varchar(max).
  6. varchar(max)+ nvarchar(n)sẽ không cắt bớt (đối với <2GB) và kết quả sẽ được nhập là nvarchar(max).
  7. nvarchar(max)+ varchar(n)đầu tiên sẽ chuyển đổi varchar(n)đầu vào thành nvarchar(n)và sau đó thực hiện nối. Nếu độ dài của varchar(n)chuỗi lớn hơn 4.000 ký tự thì quá trình nvarchar(4000)ép kiểu sẽ xảy ra và việc cắt ngắn sẽ xảy ra .

Các kiểu dữ liệu của chuỗi ký tự

Nếu bạn sử dụng Ntiền tố và chuỗi là <= dài 4.000 ký tự nó sẽ được gõ là nvarchar(n)nơi nlà chiều dài của chuỗi. Vì vậy, N'Foo'sẽ được coi là nvarchar(3)ví dụ. Nếu chuỗi dài hơn 4.000 ký tự, nó sẽ được coi lànvarchar(max)

Nếu bạn không sử dụng các Ntiền tố và chuỗi là <= dài 8.000 ký tự nó sẽ được gõ là varchar(n)nơi nlà chiều dài của chuỗi. Nếu còn nhưvarchar(max)

Đối với cả hai điều trên nếu độ dài của chuỗi bằng 0 thì nđược đặt thành 1.

Các phần tử cú pháp mới hơn.

1. Các CONCATchức năng không giúp gì đây

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

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

Ở trên trả về 8000 cho cả hai phương pháp nối.

2. Hãy cẩn thận với+=

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

Lợi nhuận

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

Lưu ý rằng @Agặp phải sự cắt ngắn.

Cách giải quyết vấn đề bạn đang gặp phải.

Bạn đang bị cắt ngắn bởi vì bạn đang nối hai maxkiểu dữ liệu không phải với nhau hoặc vì bạn đang nối một varchar(4001 - 8000)chuỗi với một nvarcharchuỗi đã nhập (chẵn nvarchar(max)).

Để tránh vấn đề thứ hai, chỉ cần đảm bảo rằng tất cả các ký tự chuỗi (hoặc ít nhất là những ký tự có độ dài trong phạm vi 4001 - 8000) được mở đầu bằng N.

Để tránh vấn đề đầu tiên, hãy thay đổi bài tập từ

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

Đến

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

để an NVARCHAR(MAX)tham gia vào quá trình nối ngay từ đầu (vì kết quả của mỗi lần nối cũng sẽ là NVARCHAR(MAX)điều này sẽ lan truyền)

Tránh cắt ngắn khi xem

Đảm bảo rằng bạn đã chọn chế độ "kết quả thành lưới" rồi bạn có thể sử dụng

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

Các tùy chọn SSMS cho phép bạn đặt độ dài không giới hạn cho XMLkết quả. Các processing-instructionchút tránh vấn đề với các nhân vật như <hiển thị như &lt;.


2
@Killercam - Bạn có thể nhận được một dàn diễn viên ngầm trong nvarchar(4000)suốt chặng đường. Nếu một chuỗi ký tự nhỏ hơn 4.000 ký tự thì nó được coi là nvarchar(x). Kết nối với nó một nvarchar(x)giá trị sẽ cắt ngắn chứ không phải bị ném lên trời đểnvarchar(max)
Martin Smith

2
@Killercam - Bạn có thể bị cắt bớt theo nhận xét đầu tiên của tôi. Hãy thử thay đổi nhiệm vụ để DECLARE @SQL NVARCHAR(MAX) = ''; SET @SQL = @SQL + một NVARCHAR(MAX)tham gia vào quá trình nối.
Martin Smith

2
@Killercam - Có thể bạn có một chuỗi từ 4.000 đến 8.000 ký tự. Với Ntiền tố đó sẽ được coi như nvarchar(max)không có nó, nó sẽ được xử lý như varchar(n)sau đó ngầm đúc đến nvarchar(4000)khi bạn nối đến mộtnvarchar
Martin Smith

3
tôi đang soi sáng bởi câu trả lời này
Mudassir Hasan

1
Câu trả lời tuyệt vời. Cám ơn rất nhiều!
John Bell,

6

Được rồi, vì vậy nếu sau này , vấn đề là bạn có một truy vấn lớn hơn kích thước cho phép (điều này có thể xảy ra nếu nó tiếp tục phát triển) thì bạn sẽ phải chia nó thành nhiều phần và thực thi các giá trị chuỗi. Vì vậy, giả sử bạn có một thủ tục được lưu trữ như sau:

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

Bạn cũng sử dụng văn bản nvarchar. điều đó có nghĩa là bạn chỉ cần có một "N" trước chuỗi lớn của bạn và thế là xong! không có giới hạn nữa

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

3
Đây không phải là toàn bộ bức tranh ... Nếu bạn sử dụng tiền tố N và chuỗi dài <= 4.000 ký tự, nó sẽ được nhập như nvarchar(n)trong đó n là độ dài của chuỗi. Vì vậy, N'Foo 'sẽ được coi là nvarchar(3)ví dụ. Nếu chuỗi dài hơn 4.000 ký tự, nó sẽ được coi là nvarchar(max). Nếu bạn không sử dụng tiền tố N và chuỗi dài <= 8.000 ký tự, nó sẽ được nhập như varchar(n)trong đó n là độ dài của chuỗi. Nếu còn như varchar(max). Đối với cả hai điều trên nếu độ dài của chuỗi bằng 0 thì n được đặt thành 1.
MoonKnight

1

Câu trả lời được chấp nhận đã giúp tôi nhưng tôi gặp khó khăn khi thực hiện nối các varchars liên quan đến các câu lệnh tình huống. Tôi biết câu hỏi của OP không liên quan đến các câu lệnh tình huống nhưng tôi nghĩ điều này sẽ hữu ích khi đăng ở đây cho những người khác như tôi, những người đã kết thúc ở đây trong khi đấu tranh để xây dựng các câu lệnh SQL động dài liên quan đến các câu lệnh tình huống.

Khi sử dụng các câu lệnh tình huống có nối chuỗi, các quy tắc được đề cập trong câu trả lời được chấp nhận áp dụng cho từng phần của câu lệnh tình huống một cách độc lập.

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.