Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
`sp_executesql`真的接受`nvarchar(max)`参数吗?_Sql_Sql Server_Sql Server 2014_Nvarchar_Limits - Fatal编程技术网

`sp_executesql`真的接受`nvarchar(max)`参数吗?

`sp_executesql`真的接受`nvarchar(max)`参数吗?,sql,sql-server,sql-server-2014,nvarchar,limits,Sql,Sql Server,Sql Server 2014,Nvarchar,Limits,摘要:对于@code中长度超过4000个的内容,EXEC sp_executesql@code将失败,但@code不会被截断为4000个unicode字符 我在SQL Server 2014开发者版上观察到这个问题 更多详细信息:我的SQL安装脚本动态定义了一些代码,因为它应该修改代码,使其反映环境(在安装期间仅一次)。让以下@datasource变量捕获特定环境的结果: DECLARE @datasource nvarchar(100) = N'testdb.dbo.source_table'

摘要:对于
@code
中长度超过4000个的内容,
EXEC sp_executesql@code
将失败,但
@code
不会被截断为4000个unicode字符

我在SQL Server 2014开发者版上观察到这个问题

更多详细信息:我的SQL安装脚本动态定义了一些代码,因为它应该修改代码,使其反映环境(在安装期间仅一次)。让以下
@datasource
变量捕获特定环境的结果:

DECLARE @datasource nvarchar(100) = N'testdb.dbo.source_table'
@code
变量声明为
nvarchar(max)
类型,并使用
REPLACE
函数根据需要修改字符串(即用
@datasource
内容替换占位符)——请参见下面的代码段

在Management Studio中使用
@code
执行
sp_executesql
时,会显示以下错误:

Msg 156,15级,状态1,程序我的sp,第86行
关键字“AS”附近的语法不正确。
Msg 102,15级,状态1,程序my_sp,第88行
“WHERE”附近的语法不正确

下面的代码片段是以上述方式失败的代码的精确副本(待复制)。功能可能并不重要——可能只是代码的长度很重要。
@code
内容显然被
sp_executesql
截断;但是,它不应该是(见下文):

请注意这两个
PRINT
命令。第一个打印头4000个字符,第二个打印其余字符。它是在中间行的,但是它只是用来表示<代码> @代码确实包含完整的字符串。 该文件说明:

[@stmt=]语句

[…]字符串的大小仅受可用数据库服务器的限制 记忆。在64位服务器上,字符串的大小限制为2 GB, nvarchar的最大大小(最大)

我在别处找到了使用
EXEC(@code)
的提示,它没有
sp\u executesql
的限制。然而,它与上述引用的文件部分相矛盾。此外,
EXEC(@code)
也不起作用

当替换后的相同内容被复制/粘贴到SQL控制台时,它工作(即创建过程)


如何解决此问题?

您的查询看起来似乎超过了nvarchar 4000的最大限制,在这种情况下,您必须将动态查询拆分为两部分

Declare @QueryA NVARCHAR(MAX),@QueryB NVARCHAR(MAX)

SET @QueryA='SELECT * FROM'
SET @QueryB=' Employee'

EXEC (@QueryA+@QueryB)

注意:如果仍然存在相同的错误,请尝试拆分为更多部分

sp_executesql不接受
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);
完整查询应如下所示:

    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);

我不知道为什么会出现错误:

Msg 156, Level 15, State 1, Procedure my_sp, Line 86
Incorrect syntax near the keyword 'AS'.
Msg 102, Level 15, State 1, Procedure my_sp, Line 88
Incorrect syntax near 'WHERE'.
被解释为字符串的长度可能太长。这显然是一个语法错误。正如埃德蒙指出的,你犯了两个错误

无论如何,我发布这个答案是为了消除另一个答案和你在问题中的建议所造成的神话,即长度是一个问题,因为你的陈述超过了4000个字符。下面是一个脚本,用于生成
100000
字符长度
NVARCHAR
SQL语句,并将其作为
EXEC(@SQL)
sp_executeSQ
L执行。在SQL 2008 SP4-OD 10.0.6547.0(x64)上执行都没有问题,在2014 SP2上也没有问题

因此,自2008年版以来,似乎没有任何问题,至少不需要解决

DECLARE @CharacterLength INT = 100000
DECLARE @SQL NVARCHAR(MAX) = 'SELECT ' + CHAR(39)

DECLARE @i INT = 1

WHILE (LEN(@SQL) <= @CharacterLength - 2)
BEGIN

    SET @SQL = @SQL + 'A'

END

SET @SQL = @SQL + CHAR(39)

PRINT 'Total Length: ' + CAST(LEN(@SQL) AS VARCHAR(100))

EXECUTE sp_executesql @sql

PRINT 'No Problem with sp_executesql'

BEGIN TRY
    PRINT 'Total Length: ' + CAST(LEN(@SQL) AS VARCHAR(100))
    EXEC (@SQL)
    PRINT 'No Problem with EXEC (@SQL)'
END TRY
BEGIN CATCH
    PRINT 'Yep never got here because there was no problem with over this character limit'
END CATCH
DECLARE@CharacterLength INT=100000
声明@SQL NVARCHAR(最大)=“选择”+字符(39)
声明@i INT=1

而(LEN(@SQL)同样的情况也困扰了我一段时间。解决方法是不声明多个NVARCHAR(MAX)变量

在构建动态SQL时,您可能会对组合到传递给sp_executesql的最终SQL查询变量中的子字符串使用NVARCHAR(MAX)

NVARCHAR(MAX)的最大内存分配为2GB。您的服务器可能正在标定每个声明的NVARCHAR(MAX)的完整2GB。如果您声明了三个NVARCHAR(MAX)变量,则服务器可能会分配6GB来执行脚本。这可能足以使RAM过载,具体取决于运行时执行的其他内容

如果您知道所有子字符串都将少于8000个字符,请使用VARCHAR(8000)而不是NVARCHAR(MAX)作为子字符串。只需使用NVARCHAR(MAX)作为传递到sp_executesql的最后一个字符串变量(所有子字符串变量组合在一起)


这就是我解决这个问题的原因。

您是在寻求解决这个问题的方法,还是(正如标题中所暗示的)询问
sp_ExecuteSQL
的内部工作机制,以及它是否真正使用了
nvarchar(max)
?你的问题似乎是问后者,但你的结束问题
如何解决这个问题?
暗示了前者。你在问什么?首先,我需要找到任何解决方案。其次,我需要澄清。医生说应该行得通,但行不通。我已经更新了片段的结尾——替代解决方案o不工作。请尝试复制/粘贴以查看。我认为您没有抓住问题的重点,但老实说很难说……整个帖子似乎都在问为什么会发生这种情况,或者它真的使用了
nvarchar(max)
?,但最后一句要求解决问题…这回答了解决问题的方法,但我认为这不是OPs想要解决的问题。在他们澄清之前,很难说清楚。谢谢,Sandip。(不,我没有否决:)我已经在代码段中添加了最后一行注释掉的行。请尝试复制/粘贴序列号