Вывод: EXEC sp_executesql @code
не работает для контента длиннее 4000 в @code
, но @code
не усекается до 4000 символов Юникода.
Я наблюдаю проблему в SQL Server 2014 Developer Edition.
Подробнее: мой сценарий установки SQL динамически определяет некоторый код, потому что он должен изменить код, чтобы он отражал среду (только один раз, во время установки). Пусть следующая переменная @datasource
фиксирует результаты для конкретной среды:
DECLARE @datasource nvarchar(100) = N'testdb.dbo.source_table'
Переменная @code
объявлена как имеющая тип nvarchar(max)
, а функция REPLACE
используется для изменения строки по мере необходимости (то есть для замены заполнителя содержимым @datasource
) — см. фрагмент ниже.
При выполнении sp_executesql
с @code
в Management Studio отображается следующая ошибка:
Сообщение 156, уровень 15, состояние 1, процедура my_sp, строка 86
Неверный синтаксис рядом с ключевым словом 'AS'.
Сообщение 102, уровень 15, состояние 1, процедура my_sp, строка 88
Неправильный синтаксис рядом с ' КУДА'.
Фрагмент ниже является точной копией кода, который не работает описанным выше способом (для воспроизведения). Функциональность, вероятно, не важна - вероятно, важна только длина кода. Содержимое @code
очевидно усекается sp_executesql
; однако этого не должно быть (см. ниже):
-- ... repeated from above
DECLARE @datasource nvarchar(100) = N'testdb.dbo.source_table'
DECLARE @code nvarchar(MAX) = REPLACE(N'
-- Comment comment comment comment comment comment comment comment comment.
-- Comment comment comment comment comment comment comment comment comment.
-- Comment comment comment comment comment comment comment comment comment.
CREATE PROCEDURE dbo.my_sp
AS
BEGIN
SET NOCOUNT ON
DECLARE @result int = -555 -- Comment comment comment comment comment.
-- Comment comment comment comment comment comment comment comment comment.
-- Comment comment comment comment comment comment comment comment comment.
DECLARE @info_table TABLE (
action nvarchar(10), -- Comment comment comment comment comment
firmaID int, -- Comment comment comment comment comment
kod numeric(8, 0), -- Comment comment comment comment comment
oz1 nvarchar(40), -- Comment comment comment comment comment
oz2 nvarchar(40), -- Comment comment comment comment comment
oz3 nvarchar(40),
oz4 nvarchar(40)
)
-- Comment comment comment comment comment comment comment comment comment.
BEGIN TRANSACTION tran_firmy
BEGIN TRY
MERGE dbo.firmy AS target
USING (SELECT kod, ico, dic, nazev,
oz1, oz2, oz3, oz4,
jeaktivni,
ulice, mesto, psc
FROM @datasource) AS source
ON target.kod = source.kod
WHEN MATCHED AND (COALESCE(target.ico, '''') != COALESCE(source.ico, '''')
OR COALESCE(target.dic, '''') != COALESCE(source.dic, '''')
OR COALESCE(target.nazev, '''') != COALESCE(source.nazev, '''')
OR COALESCE(target.nepouzivat_oz1, '''') != COALESCE(source.oz1, '''')
OR COALESCE(target.nepouzivat_oz2, '''') != COALESCE(source.oz2, '''')
OR COALESCE(target.nepouzivat_oz3, '''') != COALESCE(source.oz3, '''')
OR COALESCE(target.nepouzivat_oz4, '''') != COALESCE(source.oz4, '''')
OR COALESCE(target.jeaktivni, 0) != COALESCE(source.jeaktivni, 0)
OR COALESCE(target.ulice, '''') != COALESCE(source.ulice, '''')
OR COALESCE(target.mesto, '''') != COALESCE(source.mesto, '''')
OR COALESCE(target.psc, '''') != COALESCE(source.psc, '''')
) THEN
UPDATE
SET target.ico = source.ico,
target.dic = source.dic,
target.nazev = source.nazev,
target.nepouzivat_oz1 = source.oz1,
target.nepouzivat_oz2 = source.oz2,
target.nepouzivat_oz3 = source.oz3,
target.nepouzivat_oz4 = source.oz4,
target.jeaktivni = source.jeaktivni,
target.ulice = source.ulice,
target.mesto = source.mesto,
target.psc = source.psc,
target.changed = GETDATE(),
target.changedby = ''dialog''
WHEN NOT MATCHED THEN
INSERT (kod, ico, dic, nazev,
nepouzivat_oz1, nepouzivat_oz2, nepouzivat_oz3, nepouzivat_oz4,
jeaktivni,
ulice, mesto, psc,
created, createdby)
VALUES (source.kod, source.ico, source.dic, source.nazev,
source.oz1, source.oz2, source.oz3, source.oz4,
source.jeaktivni,
source.ulice, source.mesto, source.psc,
GETDATE(), ''dialog'')
OUTPUT
$action AS action, -- INSERT or UPDATE
inserted.ID AS firmaID,
inserted.kod AS kod,
inserted.nepouzivat_oz1 AS oz1,
inserted.nepouzivat_oz2 AS oz2,
inserted.nepouzivat_oz3 AS oz3,
inserted.nepouzivat_oz4 AS oz4
INTO @info_table;
-- Comment comment comment comment comment comment comment comment comment.
-- Comment comment comment comment comment comment comment comment comment.
SET @result = @@ROWCOUNT
-- Comment comment comment comment comment comment comment comment comment.
-- Comment comment comment comment comment comment comment comment comment.
DELETE FROM obchodni_zastupci AS ozt
WHERE ozt.kod IN (
SELECT kod FROM @info_table AS it WHER it.action = ''UPDATE''
)
-- Comment comment comment comment comment comment comment comment comment.
-- Comment comment comment comment comment comment comment comment comment.
UPDATE dodaci_adresy
SET custID = f.ID
FROM firmy AS f, dodaci_adresy AS da
WHERE da.custID IS NULL AND f.kod = da.kod_firmy
COMMIT TRANSACTION tran_firmy
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION tran_firmy
SET @result = -1 -- Comment comment comment comment comment comment comment comment comment.
END CATCH
RETURN @result -- Comment comment comment comment comment comment comment comment comment.
END', N'@datasource', N'testdb.dbo.source_table')
-- The following prints only show that the full-length string is there
PRINT SUBSTRING(@code, 0, 4000)
PRINT '-----------------------------------------------------------'
PRINT SUBSTRING(@code, 4000, 10000)
EXEC sp_executesql @code
-- The following command also does not work (uncomment it).
-- EXEC(@code)
-- Even splitting to two variables and passing the concatenation
-- does not work.
-- DECLARE @code1 nvarchar(MAX) = SUBSTRING(@code, 0, 4000)
-- DECLARE @code2 nvarchar(MAX) = SUBSTRING(@code, 4000, 10000)
-- EXEC(@code1 + @code2)
Обратите внимание на две команды PRINT
. Первый печатает первые 4000 символов, второй — остальные. Он обрезан в середине строки, но используется только для того, чтобы показать, что @code
действительно содержит полную строку.
В документации для sp_executesql (Transact-SQL) говорится:
[ @stmt= ] заявление
[...] Размер строки ограничен только доступной памятью сервера базы данных. На 64-битных серверах размер строки ограничен 2 ГБ, максимальный размер nvarchar(max).
Я нашел в другом месте намек на использование EXEC(@code)
, который не имеет ограничения sp_executesql
. Однако это противоречит приведенной выше части документации. Более того, EXEC(@code)
тоже не работает.
Когда тот же контент после замены копируется/вставляется в консоль SQL, он работает (т.е. создается процедура).
Как решить дело?
sp_ExecuteSQL
и действительно ли он используетnvarchar (max)
? Ваш вопрос, кажется, задает второе, но ваш заключительный вопросHow to solve the case?
подразумевает первое. Что вы спрашиваете? - person Siyual   schedule 29.09.2016