SQL NVARCHAR和VARCHAR限制


100

所有,我都有一个大型(不可避免的)动态SQL查询。由于选择标准中字段的数量,包含动态SQL的字符串的长度超过4000个字符。现在,我知道为设置了最大4000 NVARCHAR(MAX),但是在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);

还有其他很酷的方法吗?


3
MAX是不是为4000限制的同义词,其1..4000 MAX
亚历K.

您为什么用C#dll标记问题并进行设置,而这只是一个Sql Server问题
HatSoft 2012年

编辑。感谢您发现...
MoonKnight 2012年

PRINT将以4000个字符(对于unicode)或8000个字符(对于单字节编码)连接。我怀疑这是造成混乱的原因。
redcalx13年

Answers:


235

我了解最大可设定4000 NVARCHAR(MAX)

您的理解是错误的。nvarchar(max)最多可以存储2GB(有时甚至超过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)强制转换并将截断

字符串文字的数据类型

如果您使用N前缀,并且字符串的长度小于等于4,000个字符,则将在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));

对于两种串联方法,以上返回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)从一开始就将an 包含在串联中(因为每个串联的结果也将是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 + 以便将an NVARCHAR(MAX)包含在串联中。
马丁·史密斯

2
@Killercam-可能您的字符串在4,000到8,000个字符之间。带有N前缀的将被视为nvarchar(max)没有前缀的情况varchar(n)nvarchar(4000)当您连接至nvarchar
Martin Smith

3
我对这个答案的启发
Mudassir哈桑

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

您也要使用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

可接受的答案对我有所帮助,但是在进行涉及案例陈述的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)

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.