Ограничения SQL NVARCHAR и VARCHAR

все, у меня есть большой (неизбежный) динамический SQL-запрос. Из-за количества полей в критериях выбора строка, содержащая динамический SQL, увеличивается более чем на 4000 символов. Теперь я понимаю, что существует 4000 max set для NVARCHAR(MAX), но глядя на выполненный SQL в Профилировщике сервера для оператора

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

кажется, работает(!?), для другого запроса, который также велик, он выдает ошибку, связанную с этим пределом 4000 (!?), он в основном обрезает все SQL после этого 4000 limit и оставляет меня с синтаксической ошибкой. Несмотря на это в профилировщике, он показывает этот динамический SQL-запрос в полное(!?).

что именно здесь происходит, и я должен просто преобразовать эту переменную @SQL в VARCHAR и продолжить с ней?

Спасибо за ваше время.

Ps. Было бы также неплохо иметь возможность распечатать более 4000 символов, чтобы посмотреть на эти большие запросы. Следующие ограничиваются 4000

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

есть ли другой хороший способ?

4 ответов


я понимаю, что существует 4000 max set для NVARCHAR(MAX)

ваше понимание неверно. nvarchar(max) может хранить до (и за пределами иногда) 2 ГБ данных (1 миллиард двухбайтовых символов).

с nchar и nvarchar в книгах онлайн грамматика

nvarchar [ ( n | max ) ]

на | символ означает, что эти альтернативы. т. е. вы указываете или n или буквальном max.

если вы решите указать конкретный n тогда это должно быть между 1 и 4000, но с помощью max определяет его как тип данных большого объекта (замена для ntext, который является устаревшим).

на самом деле в SQL Server 2008 кажется, что для переменная предел 2GB может быть превышен на неопределенный срок при условии достаточного пространства в tempdb (показано здесь)

относительно других частей ваш вопрос!--62-->

усечение при объединении зависит от типа данных.

  1. varchar(n) + varchar(n) усечет на 8000 символов.
  2. nvarchar(n) + nvarchar(n) отсекает на 4000 символов.
  3. varchar(n) + nvarchar(n) отсекает на 4000 символов. nvarchar имеет более высокий приоритет, поэтому результат nvarchar(4,000)
  4. [n]varchar(max) + [n]varchar(max) не будет усекать (для
  5. varchar(max) + varchar(n) не будет усекать (для varchar(max).
  6. varchar(max) + nvarchar(n) не будет усекать (для nvarchar(max).
  7. nvarchar(max) + varchar(n) сначала преобразовать varchar(n) вход в nvarchar(n) а затем выполните конкатенацию. если длина varchar(n) строка больше, чем 4000 символов бросок будет nvarchar(4000) и произойдет усечение.

типы данных строковые литералы

если вы используете N префикс и строка длиной nvarchar(n) здесь n - длина строки. Так что N'Foo' будет рассматриваться как nvarchar(3) например. Если строка длиннее 4000 символов, она будет рассматриваться как nvarchar(max)

если вы не используете N префикс и строка длиной 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 результаты. The processing-instruction бит позволяет избежать проблем с символы, такие как < отображается как &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

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