Nvarchar (max) все еще усекается - программирование
Подтвердить что ты не робот

Nvarchar (max) все еще усекается

Итак, я пишу хранимую процедуру в 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)?

4b9b3361

Ответ 1

Чтобы увидеть динамический SQL-сгенерированный, перейдите в текстовый режим ( shortcut: 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 в конце второго выхода.

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

Ответ 2

Проблема, похоже, связана с оператором 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 )

Ответ 3

Проблема заключается в неявном преобразовании.

Если у вас есть значения 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 символов, вероятно, потому, что у вас не было Юникода, поэтому он был неявно преобразован в VarChar (8000).

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

Ответ 4

Результаты для текста допускают максимум 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*/

Ответ 5

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

Возможно, причина, по которой запрос искажен, - это нечто иное, чем усечение.

Ответ 6

Сегодня я столкнулся с той же проблемой и обнаружил, что за пределами 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)

Ответ 7

Проблема создания динамического 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).

Ответ 8

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

Но;

PRINT CAST(@query AS NTEXT)

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

Ответ 9

Используйте эту функцию 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