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)
?
Ответы
Ответ 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