nvarchar (max) все еще усекается - PullRequest
49 голосов
/ 29 января 2011

Итак, я пишу хранимую процедуру в MS SQL Server 2008. Это очень длинный запрос, и я должен написать его динамически, поэтому я создаю переменную с именем @Query и тип ее NVARCHAR(MAX). Теперь мне сказали , что в современных версиях SQL Server NVARCHAR(MAX) может содержать смешное количество данных, намного больше, чем исходный максимум в 4000 символов. Однако @Query все еще усекается до 4000 символов, когда я пытаюсь его распечатать.

DECLARE @Query NVARCHAR(max);
SET @Query = 'SELECT...' -- some of the query gets set here
SET @Query = @Query + '...' -- more query gets added on, etc.

-- later on...
PRINT LEN(@Query) -- Prints out 4273, which is correct as far as I can tell
PRINT @Query      -- Truncates value to 4000 characters
EXEC sp_executesql @Query -- totally crashes due to malformed (truncated) query

Я что-то делаю неправильно или я совершенно не прав, как работает NVARCHAR(MAX)

Ответы [ 9 ]

59 голосов
/ 20 декабря 2011

Проблема, кажется, связана с оператором SET. Я думаю, что выражение не может быть больше 4000 байт. Нет необходимости вносить какие-либо изменения в любые настройки, если все, что вы пытаетесь сделать, это назначить динамически сгенерированный оператор длиной более 4000 символов. Что вам нужно сделать, это разделить свое назначение. Если ваш оператор имеет длину 6000 символов, найдите логическую точку разрыва, а затем объедините вторую половину с той же переменной. Например:

SET @Query = 'SELECT ....' [Up To 4,000 characters, then rest of statement as below]

SET @Query = @Query + [rest of statement]

Теперь выполните ваш запрос как обычно, т.е. EXEC ( @Query )

55 голосов
/ 22 июля 2013

Проблема с неявным преобразованием.

Если у вас есть значения Unicode / nChar / nVarChar, которые вы объединяете, то SQL Server неявно преобразует вашу строку в nVarChar (4000), и, к сожалению, слишком глуп, чтобы понимать, что она будет усекать вашу строку или даже выдает предупреждение о том, что данные были усечены в этом отношении!

При объединении длинных строк (или строк, которые, по вашему мнению, могут быть длинными) всегда предварительно объедините построение строк с CAST ('' как nVarChar (MAX)) примерно так:

SET @Query = CAST('' as nVarChar(MAX))--Force implicit conversion to nVarChar(MAX)
           + 'SELECT...'-- some of the query gets set here
           + '...'-- more query gets added on, etc.

Какая боль и страшно думать, как работает SQL Server. (

Я знаю, что другие обходные пути в Интернете говорят о том, чтобы разбить ваш код на несколько назначений SET / SELECT с использованием нескольких переменных, но это не нужно, учитывая решение, приведенное выше.

Для тех, кто набрал максимум 8000 символов, возможно, потому что у вас не было Unicode, поэтому он был неявно преобразован в VarChar (8000).

Объяснение:
За кулисами происходит то, что, хотя переменная, которую вы назначаете для использования (MAX), SQL Server будет оценивать правую часть значения, которое вы назначаете в первую очередь, и по умолчанию nVarChar (4000) или VarChar (8000) (в зависимости от на что ты конкатенируешь). После того, как это сделано для определения значения (и после усечения его для вас), оно затем преобразует его в (MAX) при присвоении его вашей переменной, но к тому времени уже слишком поздно.

12 голосов
/ 29 января 2011

Чтобы увидеть сгенерированный динамический SQL, перейдите в текстовый режим ( ярлык: Ctrl-T), затем используйте SELECT

PRINT LEN(@Query) -- Prints out 4273, which is correct as far as I can tell
--SET NOCOUNT ON
SELECT @Query

Что касается sp_executesql, попробуйте это (втекстовый режим), он должен показывать три aaaaa..., средний из которых является самым длинным с добавленным «SELECT ..».Посмотрите на индикатор Ln... Col.. в строке состояния в правом нижнем углу, показывая 4510 в конце 2-го выхода.

declare @n nvarchar(max)
set @n = REPLICATE(convert(nvarchar(max), 'a'), 4500)
SET @N = 'SELECT ''' + @n + ''''
print @n   -- up to 4000
select @n  -- up to max
exec sp_Executesql @n
5 голосов
/ 29 января 2011

Результаты в тексте допускают не более 8192 символов.

Screenshot

Я использую этот подход

DECLARE @Query NVARCHAR(max);

set @Query = REPLICATE('A',4000)
set @Query = @Query + REPLICATE('B',4000)
set @Query = @Query + REPLICATE('C',4000)
set @Query = @Query + REPLICATE('D',4000)

select LEN(@Query)

SELECT @Query /*Won't contain any "D"s*/
SELECT @Query as [processing-instruction(x)] FOR XML PATH /*Not truncated*/
4 голосов
/ 29 января 2011

Ваша первая проблема - ограничение оператора PRINT. Я не уверен, почему sp_executesql терпит неудачу. Он должен поддерживать практически любую длину ввода.

Возможно, причина неправильного запроса - не усечение.

2 голосов
/ 21 марта 2018

Печать сокращает varchar (MAX) до 8000, nvarchar (MAX) до 4000 символов.

Но,

PRINT CAST(@query AS NTEXT)

напечатает весь запрос.

1 голос
/ 24 апреля 2018

Проблема с созданием динамического SQL с использованием строкового выражения состоит в том, что SQL действительно ограничивает вычисление строковых выражений до 4000 символов. Вы можете назначить более длинную строку переменной nvarchar (max), но как только вы включите + в выражение (например, + CASE ... END +), результат выражения будет ограничен 4000 символов.

Один из способов исправить это - использовать CONCAT вместо +. Например:

SET @sql = CONCAT(@sql, N'
     ... dynamic SQL statements ...
    ', CASE ... END, N'
     ... dynamic SQL statements ...
    ')

Где @sql объявлен как nvarchar (max).

1 голос
/ 26 мая 2016

Используйте эту функцию PRINT BIG для вывода всего:

IF OBJECT_ID('tempdb..#printBig') IS NOT NULL
  DROP PROCEDURE #printBig

GO

CREATE PROCEDURE #printBig (
   @text NVARCHAR(MAX)
 )
AS

--DECLARE @text NVARCHAR(MAX) = 'YourTextHere'
DECLARE @lineSep NVARCHAR(2) = CHAR(13) + CHAR(10)  -- Windows \r\n

DECLARE @off INT = 1
DECLARE @maxLen INT = 4000
DECLARE @len INT

WHILE @off < LEN(@text)
BEGIN

  SELECT @len =
    CASE
      WHEN LEN(@text) - @off - 1 <= @maxLen THEN LEN(@text)
      ELSE @maxLen
             - CHARINDEX(REVERSE(@lineSep),  REVERSE(SUBSTRING(@text, @off, @maxLen)))
             - LEN(@lineSep)
             + 1
    END
  PRINT SUBSTRING(@text, @off, @len)
  --PRINT '@off=' + CAST(@off AS VARCHAR) + ' @len=' + CAST(@len AS VARCHAR)
  SET @off += @len + LEN(@lineSep)

END

Источник:

https://www.richardswinbank.net/doku.php?id=tsql:print_big

1 голос
/ 13 декабря 2011

Я столкнулся с той же проблемой сегодня и обнаружил, что за пределами этого ограничения в 4000 символов мне пришлось разбить динамический запрос на две строки и объединить их при выполнении запроса.

DECLARE @Query NVARCHAR(max);
DECLARE @Query2 NVARCHAR(max);
SET @Query = 'SELECT...' -- some of the query gets set here
SET @Query2 = '...' -- more query gets added on, etc.

EXEC (@Query + @Query2)
...