簡體   English   中英

SQL NVARCHAR 和 VARCHAR 限制

[英]SQL NVARCHAR and VARCHAR Limits

所有,我有一個大的(不可避免的)動態 SQL 查詢。 由於選擇標准中的字段數量,包含動態 SQL 的字符串增長到超過 4000 個字符。 現在,我知道NVARCHAR(MAX)最大設置為 4000,但是查看 Server Profiler 中執行的 SQL 語句

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

似乎有效(!?),對於另一個也很大的查詢,它會引發與此 4000 限制相關的錯誤(!?),它基本上會在此 4000 限制之后修剪所有 SQL,並給我留下語法錯誤。 盡管這樣的探查,它顯示在這個充滿動態的SQL查詢(!?)。

這里到底發生了什么,我應該將這個@SQL 變量轉換為 VARCHAR 並繼續嗎?

謝謝你的時間。

附言。 能夠打印出超過 4000 個字符來查看這些大查詢也很好。 以下限制為 4000

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

還有其他很酷的方法嗎?

我知道NVARCHAR(MAX)最大設置為 4000

你的理解是錯誤的。 nvarchar(max)可以存儲(有時甚至超過)2GB 的數據(10 億個雙字節字符)。

從在線書籍中的nchar 和 nvarchar語法是

nvarchar [ ( n | max ) ]

| 字符意味着這些是替代品。 指定n或文字max

如果您選擇指定特定的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)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'

以便NVARCHAR(MAX)從一開始就參與連接(因為每個連接的結果也將是NVARCHAR(MAX)這將傳播)

查看時避免截斷

確保您選擇了“結果到網格”模式,然后您可以使用

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

SSMS 選項允許您為XML結果設置無限長度。 processing-instruction位避免了諸如<顯示為&lt;等字符的問題&lt; .

好的,所以如果后面的問題是您的查詢大於允許的大小(如果它不斷增長可能會發生這種情況),您將不得不將其分成塊並執行字符串值。 因此,假設您有一個如下所示的存儲過程:

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

您也必須使用 nvarchar 文本。 這意味着你必須在你的大字符串之前簡單地有一個“N”,就是這樣! 沒有限制了

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

接受的答案對我有幫助,但在連接涉及 case 語句的 varchars 時我被絆倒了。 我知道 OP 的問題不涉及 case 語句,但我認為這對於像我這樣在努力構建涉及 case 語句的長動態 SQL 語句時最終來到這里的其他人來說會有所幫助。

當使用帶有字符串連接的 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)
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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM