103

All, I have a large (unavoidable) dynamic SQL query. Due to the number of fields in the selection criteria the string containing the dynamic SQL is growing over 4000 chars. Now, I understand that there is a 4000 max set for NVARCHAR(MAX), but looking at the executed SQL in Server Profiler for the statement

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

Seems to work(!?), for another query that is also large it throws an error which is associated with this 4000 limit(!?), it basically trims all of the SQL after this 4000 limit and leaves me with a syntax error. Despite this in the profiler, it is showing this dynamic SQL query in full(!?).

What exactly is happening here and should I just be converting this @SQL variable to VARCHAR and get on with it?

Thanks for your time.

Ps. It would also be nice to be able to print out more than 4000 chars to look at these big queries. The following are limited to 4000

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

is there any other cool way?

4

5 回答 5

240

我知道最大设置为 4000NVARCHAR(MAX)

你的理解是错误的。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 个字符,则强制转换为 tonvarchar(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'

这样 anNVARCHAR(MAX)从一开始就参与连接(因为每个连接的结果也将是NVARCHAR(MAX)this 将传播)

查看时避免截断

确保您选择了“结果到网格”模式,然后您可以使用

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

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

于 2012-09-28T12:23:26.190 回答
7

好的,所以如果稍后问题是您有一个大于允许大小的查询(如果它不断增长,可能会发生这种情况),您将不得不将它分成块并执行字符串值。因此,假设您有一个如下所示的存储过程:

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
于 2012-09-28T12:41:51.463 回答
2

您也必须使用 nvarchar 文本。这意味着你必须在你的大弦之前简单地有一个“N”,就是这样!不再有限制

DELARE @SQL NVARCHAR(MAX);
SET @SQL = N'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO
于 2015-06-18T07:32:35.773 回答
1

接受的答案对我有帮助,但我在连接涉及案例陈述的 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)
于 2019-04-09T13:51:59.623 回答
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
于 2017-03-23T08:18:27.670 回答