SQL NVARCHAR和VARCHAR限制

Moo*_*ght 97 sql sql-server variables string-concatenation sql-server-2008

总之,我有一个很大的(不可避免的)动态SQL查询.由于选择标准中的字段数,包含动态SQL的字符串增长超过4000个字符.现在,我知道有一个4000最大值设置NVARCHAR(MAX),但查看Server Profiler中执行的SQL语句

DELARE @SQL NVARCHAR(MAX);
SET @SQL = 'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO
Run Code Online (Sandbox Code Playgroud)

似乎工作(!?),对于另一个同样大的查询,它会抛出一个与此4000限制(!?)相关的错误,它基本上修剪了这个4000限制之后的所有SQL,并且给我留下了语法错误.尽管这样的探查,它表示在这个动态的SQL查询(!?).

究竟发生了什么,我应该将这个@SQL变量转换为VARCHAR并继续使用它吗?

谢谢你的时间.

PS.能够打印超过4000个字符来查看这些大查询也是很好的.以下限制为4000

SELECT CONVERT(XML, @SQL);
PRINT(@SQL);
Run Code Online (Sandbox Code Playgroud)

还有其他很酷的方式吗?

Mar*_*ith 227

据我所知,最大设置为4000 NVARCHAR(MAX)

你的理解是错误的.nvarchar(max)可存储最多(有时超过2GB)数据(10亿个双字节字符).

从在线书籍中的nchar和nvarchar语法是

nvarchar [ ( n | max ) ]
Run Code Online (Sandbox Code Playgroud)

|字符意味着这些替代品.即你指定其中一个 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)并且将发生截断.

字符串文字的数据类型

如果使用N前缀和该字符串是<= 4000个字符长它将被分类为nvarchar(n)其中n是串的长度.因此N'Foo'将被视为nvarchar(3)例如.如果字符串超过4,000个字符,则将其视为nvarchar(max)

如果不使用N前缀和字符串<= 8000个字符长,将分型为varchar(n)其中n是字符串的长度.如果更长varchar(max)

对于上述两种情况,如果字符串的长度为零,则n设置为1.

较新的语法元素.

1.CONCAT功能不会在这里帮助

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

SELECT DATALENGTH(@A5000 + @A5000), 
       DATALENGTH(CONCAT(@A5000,@A5000));
Run Code Online (Sandbox Code Playgroud)

以上两种连接方法都返回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);`
Run Code Online (Sandbox Code Playgroud)

返回

-------------------- --------------------
8000                 10000
Run Code Online (Sandbox Code Playgroud)

请注意@A遇到截断.

如何解决您遇到的问题.

您正在截断,因为您将两个非max数据类型连接在一起,或者因为您将varchar(4001 - 8000)字符串连接到一个nvarchar类型化的字符串(偶数nvarchar(max)).

为避免第二个问题,只需确保所有字符串文字(或至少长度在4001 - 8000范围内的字符串文字)都以N.

要避免第一个问题,请更改分配

DECLARE @SQL NVARCHAR(MAX);
SET @SQL = 'Foo' + 'Bar' + ...;
Run Code Online (Sandbox Code Playgroud)

DECLARE @SQL NVARCHAR(MAX) = ''; 
SET @SQL = @SQL + N'Foo' + N'Bar'
Run Code Online (Sandbox Code Playgroud)

所以a NVARCHAR(MAX)从一开始就参与连接(因为每个连接的结果也NVARCHAR(MAX)将会传播)

查看时避免截断

确保选择了"结果到网格"模式,然后才能使用

select @SQL as [processing-instruction(x)] FOR XML PATH 
Run Code Online (Sandbox Code Playgroud)

SSMS选项允许您为XML结果设置无限长度.该processing-instruction位避免出现诸如<显示为的字符问题&lt;.

  • 我得到了这个答案的启发 (3认同)
  • @Killercam - 在此过程中,您可能会隐式转换为 `nvarchar(4000)`。如果字符串文字少于 4,000 个字符,则将其视为 `nvarchar(x)`。连接另一个 `nvarchar(x)` 值将截断而不是向上转换为 `nvarchar(max)` (2认同)
  • @Killercam - 根据我的第一条评论,您可能会被截断。尝试将分配更改为`DECLARE @SQL NVARCHAR(MAX) = ''; SET @SQL = @SQL + ` 以便在连接中包含一个 `NVARCHAR(MAX)`。 (2认同)
  • @Killercam - 可能你有一个介于4,000到8,000个字符之间的字符串.使用`N`前缀将被视为`nvarchar(max)`而没有它它将被视为`varchar(n)`然后在连接到`nvarchar时'隐式地转换为`nvarchar(4000)` (2认同)

Mik*_*oud 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
Run Code Online (Sandbox Code Playgroud)