Обмеження SQL NVARCHAR та VARCHAR


100

Все, у мене великий (неминучий) динамічний запит SQL. Завдяки кількості полів у критеріях вибору рядок, що містить динамічний SQL, зростає понад 4000 символів. Тепер я розумію, що існує 4000 max для NVARCHAR(MAX), але дивлячись на виконаний SQL в Server Profiler для заяви

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

Здається, що працює (!?), для іншого великого запиту він видає помилку, пов’язану з цим обмеженням 4000 (!?), він в основному обрізає весь SQL після цього обмеження 4000 і залишає мене з синтаксичною помилкою. Незважаючи на це у профіле, він відображає цей динамічний запит SQL повністю (!?).

Що саме тут відбувається, і чи варто мені просто перетворити цю змінну @SQL у VARCHAR і продовжувати її?

Дякую за ваш час.

Пс. Було б також непогано мати можливість роздрукувати більше 4000 символів, щоб переглянути ці великі запити. Далі обмежено 4000

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

чи є ще якийсь крутий спосіб?


3
MAX не є синонімом межі 4000, його 1..4000 або MAX
Алекс К.

Чому ви позначили це питання на C # dll & налаштування swhen, це лише питання сервера Sql
HatSoft

Відредаговано. Дякуємо, що
помітили

PRINT об'єднається в 4000 символів (для unicode) або 8000 символів (для однобайтових кодувань). Я підозрюю, що це джерело плутанини тут.
redcalx

Відповіді:


235

Я розумію, що існує 4000 максимум для NVARCHAR(MAX)

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

Із nchar і nvarchar у Книгах онлайн граматика

nvarchar [ ( n | max ) ]

На |характер означає , що ці альтернативи. тобто вказати або n або буквальним max.

Якщо ви вирішите вказати конкретний, nто це повинно бути від 1 до 4 000, але використання maxвизначає його як великий тип даних об'єкта (заміна на ntextякий застаріла).

Насправді в SQL Server 2008 здається, що для змінної ліміт 2 Гб може бути перевищений на невизначений термін за умови достатнього місця в tempdb( Показано тут )

Щодо інших частин вашого питання

Скорочення при об'єднанні залежить від типу даних.

  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)не врізається (для <2 Гб).
  5. varchar(max)+ varchar(n)не врізається (для <2 Гб), і результат буде набрано як varchar(max).
  6. varchar(max)+ nvarchar(n)не врізається (для <2 Гб), і результат буде набрано як nvarchar(max).
  7. nvarchar(max)+ varchar(n)спочатку перетворить varchar(n)вхід, nvarchar(n)а потім зробить конкатенацію. Якщо довжина varchar(n)рядка перевищує 4000 символів, буде nvarchar(4000)відкинутись і відбудеться усічення .

Типи даних рядкових літералів

Якщо ви використовуєте Nпрефікс, а рядок дорівнює <= 4000 символів, він буде введений як і nvarchar(n)де nдовжина рядка. Так N'Foo'поводиться, як nvarchar(3)наприклад. Якщо рядок довший 4000 символів, він буде розглядатися як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'

таким чином, що an NVARCHAR(MAX)залучається до конкатенації з самого початку (як результат кожного конкатенації також буде NVARCHAR(MAX)це поширюватися)

Уникаючи усікання під час перегляду

Переконайтесь, що у вас вибрано режим "результати на сітку", щоб потім можна було користуватися

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

Параметри SSMS дозволяють встановлювати необмежену довжину для XMLрезультатів. processing-instructionБіт дозволяє уникнути проблем з символами , такими як <показ , як &lt;.


2
@Killercam - Ви, можливо, nvarchar(4000)по дорозі отримуєте неявну роль . Якщо буквений рядок менше 4000 символів, то це трактується як nvarchar(x). Приєднання до нього іншого nvarchar(x)значення буде скорочуватися, а не перетворюватися наnvarchar(max)
Мартін Сміт

2
@Killercam - Ви, мабуть, отримуєте усічення згідно мого першого коментаря. Спробуйте змінити призначення DECLARE @SQL NVARCHAR(MAX) = ''; SET @SQL = @SQL + так, щоб NVARCHAR(MAX)в конкатенації був залучений ан .
Мартін Сміт

2
@Killercam - Можливо, у вас є рядок між 4000 і 8000 символами. З Nпрефіксом, який буде розглядатися як nvarchar(max)без нього, він буде розглядатися так, як varchar(n)тоді імпліцитно передається, nvarchar(4000)коли ви приєднаєтесь доnvarchar
Мартін Сміт,

3
Мене просвітить ця відповідь
Мудассір Хасан

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, а рядок довжиною <= 4000 символів, він буде набраний так, nvarchar(n)де n - довжина рядка. Тож до N'Foo трапляються, як nvarchar(3)наприклад. Якщо рядок довший 4000 символів, він буде розглядатися як nvarchar(max). Якщо ви не використовуєте N префікса, а рядок становить <= 8000 символів, він буде набраний так, varchar(n)де n - довжина рядка. Якщо довше як varchar(max). Для обох вищезазначених, якщо довжина струни дорівнює нулю, то n встановлено на 1.
MoonKnight

1

Прийнята відповідь допомогла мені, але я зіткнувся, роблячи конкатенацію вархарів із заявами справи. Я знаю, що питання ОП не стосується тверджень про випадки, але я вважав, що це буде корисним для публікації тут для інших, як я, які опинилися тут, намагаючись створити довгі динамічні оператори SQL, що стосуються випадок справи.

При використанні випадок справи зі строковим конкатенацією правила, згадані у прийнятій відповіді, застосовуються до кожного розділу оператора справи незалежно.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.