SQL NVARCHAR和VARCHAR限制

时间:2012-09-28 12:22:12

标签: sql sql-server sql-server-2008 variables string-concatenation

全部,我有一个很大的(不可避免的)动态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);

还有其他很酷的方式吗?

5 个答案:

答案 0 :(得分:222)

  

我知道NVARCHAR(MAX)

最多设置了4000

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

来自联机丛书中的nchar and nvarchar语法

nvarchar [ ( n | max ) ]

|字符表示这些是替代品。即你指定 n或文字max

如果您选择指定特定的n,那么它必须介于1到4,000之间,但使用max将其定义为大对象数据类型(替换为ntext,不推荐使用)。

事实上,在SQL Server 2008中,似乎对于变量tempdbShown here

可以无限期地超出2GB限制

关于问题的其他部分

连接时截断取决于数据类型。

  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)不会截断(对于&lt; 2GB),结果将被输入为nvarchar(max)
  7. nvarchar(max) + varchar(n)会先将varchar(n)输入转换为nvarchar(n),然后再进行连接。 如果varchar(n)字符串的长度超过4,000个字符,则广告素材将显示为nvarchar(4000),并且会发生截断
  8. 字符串文字的数据类型

    如果您使用N前缀且字符串长度为&lt; = 4,000个字符,则会将其键入nvarchar(n),其中n是字符串的长度。因此,N'Foo'将被视为nvarchar(3)。如果字符串超过4,000个字符,则将其视为nvarchar(max)

    如果您不使用N前缀且字符串长度为&lt; = 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;的问题。

答案 1 :(得分: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 :(得分:2)

你也使用nvarchar文本。这意味着你必须简单地拥有一个&#34; N&#34;在你的大量字符串之前它就是它!没有限制

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

答案 3 :(得分:1)

被接受的答案对我有所帮助,但是在进行涉及案例陈述的varchar串联时,我绊倒了。我知道OP的问题不涉及案例语句,但是我认为这对于将像我这样的其他人张贴在这里是有帮助的,他们最终在这里努力构建涉及案例语句的长动态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)

答案 4 :(得分: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