摘要: 在EXEC sp_executesql@code失败的含量比4000更长@code,但@code不截断为4000个Unicode字符。
EXEC sp_executesql@code
@code
我正在观察SQL Server 2014 Developer Edition上的问题。
更多详细信息:我的SQL安装脚本动态定义了一些代码,因为它应该修改代码以使其反映环境(在安装过程中仅一次)。让以下@datasource变量捕获特定环境的结果:
@datasource
DECLARE @datasource nvarchar(100) = N'testdb.dbo.source_table'
该@code变量声明为nvarchar(max)类型,并且该REPLACE函数用于根据需要修改字符串(即用@datasource内容替换占位符)-请参见下面的代码段。
nvarchar(max)
REPLACE
在Management Studio中执行sp_executesql时@code,将显示以下错误:
sp_executesql
消息156,级别15,状态1,过程my_sp,第86行 关键字“ AS”附近的语法不正确。 消息102,级别15,状态1,过程my_sp,第88行 ‘WHERE’附近的语法不正确。
下面的代码段是上述方法失败的精确代码副本(将被复制)。功能可能并不重要- 可能只有代码的长度是重要的。的@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真正包含完整的字符串。
PRINT
sp_executesql(Transact-SQL)的文档说:
[@ stmt =]语句 […]字符串的大小仅受可用的数据库服务器内存限制。在64位服务器上,字符串的大小限制为2 GB,即nvarchar(max)的最大大小。
[@ stmt =]语句
[…]字符串的大小仅受可用的数据库服务器内存限制。在64位服务器上,字符串的大小限制为2 GB,即nvarchar(max)的最大大小。
我在其他地方找到了EXEC(@code)没有限制的使用提示sp_executesql。但是,它与文档中上面引用的部分相矛盾。而且,这EXEC(@code)也不起作用。
EXEC(@code)
将替换后的相同内容复制/粘贴到SQL控制台后,它便起作用(即创建了过程)。
该如何解决?
sp_executesql确实接受NVARCHAR(MAX)。问题在于查询语句的以下语句中存在错误:
NVARCHAR(MAX)
DELETE FROM obchodni_zastupci AS ozt WHERE ozt.kod IN ( SELECT kod FROM @info_table AS it WHER it.action = ''UPDATE'' )
应为:如下:
DELETE FROM obchodni_zastupci WHERE obchodni_zastupci.kod IN ( SELECT kod FROM @info_table AS it WHERE it.action = ''UPDATE'' )
完整的查询应如下所示:
DECLARE @datasource nvarchar(100) = N'testdb.dbo.source_table' DECLARE @template NVARCHAR(MAX) = 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 WHERE obchodni_zastupci.kod IN ( SELECT kod FROM @info_table AS it WHERE 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' DECLARE @code nvarchar(MAX) = REPLACE(@template, N'@datasource', N'testdb.dbo.source_table'); exec (@code);