sp_executesql 不使用参数

Aus*_*tin 5 sql-server dynamic-sql t-sql

我遇到了一个奇怪的动态 sql 问题,我认为这是某种格式问题,在过去的几个小时里我一直在为此抓狂。我非常感谢我能得到的任何帮助。

对于一些背景信息,此存储过程的目的是清理我们的集中式 (hq) 数据库中的数据,该数据库应该拥有我们所有商店的所有记录的完整副本。如果重要的话,我们正在运行 Microsoft Dynamics RMS (2.0.2022) 和 SQL 2014 Standard(对于总部,商店使用 Express)。

最初我打算将 sql 语句放在参数中并使用 exec 命令,但是虽然我不经常使用它,但我认为 sp_executesql 看起来更干净,并且更容易将查询重用为模板并进行更改他们。现在我只有一些选择的脚本,但是一旦它们完成,我将创建一个额外的存储过程来插入、删除和更新记录。

执行 sql 语句有效,但出于某种原因,我无法让 sp_executesql 使用存储过程中的参数。无论我将参数更改为什么,包括在存储过程中使用静态值,它都只会输出参数名称。所以无论:

@LS = @LinkedServer
Run Code Online (Sandbox Code Playgroud)

或者

@LS = 'StaticLinkedServerName'
Run Code Online (Sandbox Code Playgroud)

它返回的错误信息是:

在 sys.servers 中找不到服务器“@LS”。验证是否指定了正确的服务器名称。如有必要,执行存储过程 sp_addlinkedserver 将服务器添加到 sys.servers。

无效的解决方案

CREATE PROCEDURE [dbo].[usp_bn_ShowStoreHQSync]  
(
      @StoreCode        NVARCHAR(10)
     ,@LinkedServer     NVARCHAR(50)    
     ,@StoreDB          NVARCHAR(40)
)
AS          
BEGIN   
    BEGIN TRANSACTION
        BEGIN TRY
            IF @StoreCode IS NULL
                RAISERROR('@StoreCode Cannot Be NULL',15,1)
            ELSE IF @StoreCode NOT IN (SELECT StoreCode FROM [HQDB].[dbo].[Store])
                RAISERROR('@StoreCode Not In DB',15,1)
            ELSE IF @LinkedServer IS NULL
                RAISERROR('@LinkedServer Cannot Be NULL',15,1)
            ELSE IF @LinkedServer NOT IN (SELECT name FROM sys.servers)
                RAISERROR('@LinkedServer Does Not Exist or Cannot Connect',15,1)
            ELSE
                DECLARE @LinkedServerCatalog AS TABLE
                (
                    DBName NVARCHAR(MAX)
                    , Description NVARCHAR(MAX)
                )
                INSERT INTO @LinkedServerCatalog
                EXEC sp_catalogs @server_name = @LinkedServer
            
                IF @StoreDB NOT IN (SELECT DBName FROM @LinkedServerCatalog)
                    RAISERROR('@Database Does Not Exist or Cannot Connect',15,1)
                ELSE
                    DECLARE @StoreID INT;
                    DECLARE @Last401 DATETIME;

                    SET @StoreID = (SELECT ID FROM Store WHERE StoreCode = @StoreCode)

                    SET @Last401 = dbo.Last401(@StoreID)

                    EXECUTE sp_executesql   
                    N'SELECT SOPO.ID
                        , SOPO.ID
                        , SOPO.PONumber
                        , SOPO.POTitle
                        , SOPO.Status
                        , SOPO.LastUpdated
                        , SOPO.DateCreated
                        , SOPO.DatePlaced
                        , SOPO.IsPlaced
                        , SOPO.Remarks
                        , SOPO.StoreID
                        , SOPO.OtherStoreID
                        , SOPO.[To]
                        , SOPO.ShipTo
                        , SOPO.WorksheetID
                        FROM [@LS].[@DB].[dbo].[PurchaseOrder] SOPO
                        WHERE SOPO.ID NOT IN (SELECT HQPO.ID FROM HQDB.dbo.PurchaseOrder HQPO WHERE HQPO.StoreID = @SID)'  
                    , N'@LS NVARCHAR(50), @DB NVARCHAR(40), @SID NVARCHAR(10)'
                    , @LS = @LinkedServer
                    , @DB = @StoreDB
                    , @SID = @StoreID;

        COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
        SELECT
        ERROR_NUMBER() AS ErrorNumber
        , ERROR_STATE() AS ErrorState
        , ERROR_SEVERITY() AS ErrorSeverity
        , ERROR_PROCEDURE() AS ErrorProcedure
        , ERROR_LINE() AS ErrorLine
        , ERROR_MESSAGE() AS ErrorMessage;

        ROLLBACK TRANSACTION;
    END CATCH
END
Run Code Online (Sandbox Code Playgroud)

工作方案

CREATE PROCEDURE [dbo].[usp_bn_ShowStoreHQSync]  
(
      @StoreCode        NVARCHAR(10)
     ,@LinkedServer     NVARCHAR(50)    
     ,@StoreDB          NVARCHAR(40)
)
AS          
BEGIN   
    BEGIN TRANSACTION
        BEGIN TRY
            IF @StoreCode IS NULL
                RAISERROR('@StoreCode Cannot Be NULL',15,1)
            ELSE IF @StoreCode NOT IN (SELECT StoreCode FROM [HQDB].[dbo].[Store])
                RAISERROR('@StoreCode Not In DB',15,1)
            ELSE IF @LinkedServer IS NULL
                RAISERROR('@LinkedServer Cannot Be NULL',15,1)
            ELSE IF @LinkedServer NOT IN (SELECT name FROM sys.servers)
                RAISERROR('@LinkedServer Does Not Exist or Cannot Connect',15,1)
            ELSE
                DECLARE @LinkedServerCatalog AS TABLE
                (
                    DBName NVARCHAR(MAX)
                    , Description NVARCHAR(MAX)
                )
                INSERT INTO @LinkedServerCatalog
                EXEC sp_catalogs @server_name = @LinkedServer
            
                IF @StoreDB NOT IN (SELECT DBName FROM @LinkedServerCatalog)
                    RAISERROR('@Database Does Not Exist or Cannot Connect',15,1)
                ELSE
                    DECLARE @StoreID INT;
                    DECLARE @Last401 DATETIME;

                    DECLARE @SQL_MissingPO NVARCHAR(MAX);

                    SET @StoreID = (SELECT ID FROM Store WHERE StoreCode = @StoreCode)

                    SET @Last401 = dbo.Last401(@StoreID)

                    SET @SQL_MissingPO = '  SELECT ''@SQL_MissingPO'' AS ''Script''
                                            , SOPO.ID
                                            , SOPO.PONumber
                                            , SOPO.POTitle
                                            , SOPO.Status
                                            , SOPO.LastUpdated
                                            , SOPO.DateCreated
                                            , SOPO.DatePlaced
                                            , SOPO.IsPlaced
                                            , SOPO.Remarks
                                            , SOPO.StoreID
                                            , SOPO.OtherStoreID
                                            , SOPO.[To]
                                            , SOPO.ShipTo
                                            , SOPO.WorksheetID
                                            FROM [' + @LinkedServer + '].[' + @StoreDB + '].[dbo].[PurchaseOrder] SOPO
                                            WHERE SOPO.ID NOT IN (SELECT HQPO.ID FROM HQDB.dbo.PurchaseOrder HQPO WHERE HQPO.StoreID = ' + CAST(@StoreID AS NVARCHAR(20)) + ')'
                                            
                    EXEC (@SQL_MissingPO)

        COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
        SELECT
        ERROR_NUMBER() AS ErrorNumber
        , ERROR_STATE() AS ErrorState
        , ERROR_SEVERITY() AS ErrorSeverity
        , ERROR_PROCEDURE() AS ErrorProcedure
        , ERROR_LINE() AS ErrorLine
        , ERROR_MESSAGE() AS ErrorMessage;

        ROLLBACK TRANSACTION;
    END CATCH
END;
Run Code Online (Sandbox Code Playgroud)

执行命令(对于两种解决方案)

EXEC usp_bn_ShowStoreHQSync '01', 'Store_001', 'bnstr01'
Run Code Online (Sandbox Code Playgroud)

Eri*_*ing 11

厄兰

您不能使用对象名称作为动态 SQL 中的参数。这就是为什么你的第一次尝试不起作用

如果将第一次和第二次尝试结合起来,至少可以对@StoreId参数进行参数化。

另外,请记住,仅使用方括号并不完全安全。您需要使用QUOTENAME

DECLARE
    @sql nvarchar(MAX) = N'';

SELECT
   @sql += N'
SELECT 
    SOPO.ID
    SOPO.ID,
    SOPO.PONumber,
    SOPO.POTitle,
    SOPO.Status,
    SOPO.LastUpdated,
    SOPO.DateCreated,
    SOPO.DatePlaced,
    SOPO.IsPlaced,
    SOPO.Remarks,
    SOPO.StoreID,
    SOPO.OtherStoreID,
    SOPO.[To],
    SOPO.ShipTo,
    SOPO.WorksheetID
FROM ' + 
QUOTENAME(@LS) + 
N'.' +
QUOTENAME(@DB) +
N'.dbo.PurchaseOrder SOPO
WHERE SOPO.ID NOT IN (SELECT HQPO.ID FROM HQDB.dbo.PurchaseOrder HQPO WHERE HQPO.StoreID = @SID);';  
                                                        
EXECUTE sys.sp_executesql   
    @sql,
    N'@SID NVARCHAR(10)',
    @SID = @StoreID;
Run Code Online (Sandbox Code Playgroud)