如何从SP_ExecuteSQL返回表输出

时间:2016-08-25 10:38:02

标签: sql-server tsql stored-procedures

我试图创建一个动态生成SQL加载的存储过程。我试图通过使用sp_executesql将所有参数传递给查询来防止字符串注入。查询似乎执行正常,但我有两个问题。首先,当我使用我的Alias名称执行使用模式xx\xxxxx时,它会抛出一个错误,说Alias的前两个字母附近有错误。我认为这是由于NVARCHAR类型,但不完全确定如何处理\。其次,更重要的是,整个程序一无所获。它告诉我它已经执行得很好但我没有按预期得到我的结果表。

为了简洁,我省略了生成参数的语句(如果我生成整个字符串并运行EXEC string

,这些工作正常

我也担心使用全局临时表,因为它将在多用户环境中运行

已更新为完整代码

DECLARE @IDType NVARCHAR(255) = NULL
DECLARE @Customer NVARCHAR(MAX) = NULL
DECLARE @IdentifiedBy NVARCHAR(255) = NULL
DECLARE @ImpactArea NVARCHAR(MAX) = NULL
DECLARE @Gateway NVARCHAR(255) = NULL
DECLARE @ProbabilityRating NVARCHAR(255) = NULL
DECLARE @ImpactRating NVARCHAR(255) = NULL
DECLARE @CostRevenue NVARCHAR(255) = NULL
DECLARE @Status NVARCHAR(255) = NULL
DECLARE @Keywords NVARCHAR(MAX) = NULL
DECLARE @govOwner NVARCHAR(255) = NULL

DECLARE @Alias VARCHAR(50)
DECLARE @Role NVARCHAR(255)
DECLARE @SQL NVARCHAR(MAX)
DECLARE @Where NVARCHAR(MAX) = ''
DECLARE @SQLOrder NVARCHAR(MAX)

SET @Alias = SUSER_SNAME()
SET @Role =(SELECT [Role] FROM [FB].[Users] WHERE [Alias] = @Alias)

IF @IDType IS NOT NULL
BEGIN
IF @IDType = 'Blank'
    SET @Where += ' AND IDType = NULL OR IDType = '''''
ELSE IF @IDType != 'All'
    SET @Where += ' AND IDType = @IDType'
END
IF @Customer IS NOT NULL
BEGIN
IF @Customer = 'Blank'
    SET @Where += ' AND Customer = NULL OR Customer = '''''
ELSE IF @Customer != 'All'
    SET @Where += ' AND Customer = @Customer'
END
IF @IdentifiedBy IS NOT NULL
BEGIN
IF @IdentifiedBy = 'Blank'
    SET @Where += ' AND IdentifiedBy = NULL OR IdentifiedBy = '''''
ELSE IF @IdentifiedBy != 'All'
    SET @Where += ' AND IdentifiedBy = (SELECT FB.Alias(@IdentifiedBy))'
END
IF @ImpactArea IS NOT NULL
BEGIN
IF @ImpactArea = 'Blank'
    SET @Where += ' AND ImpactArea = NULL OR ImpactArea = '''''
ELSE IF @ImpactArea != 'All'
    SET @Where += ' AND ImpactArea = @ImpactArea'
END
IF @Gateway IS NOT NULL
BEGIN
IF @Gateway = 'Blank'
    SET @Where += ' AND Gateway = NULL OR Gateway = '''''
ELSE IF @Gateway != 'All'
    SET @Where += ' AND Gateway = @Gateway'
END
IF @ProbabilityRating IS NOT NULL
BEGIN
IF @ProbabilityRating = 'Blank'
    SET @Where += ' AND ProbabilityRating = NULL OR ProbabilityRating = '''''
ELSE IF @ProbabilityRating != 'All'
    SET @Where += ' AND ProbabilityRating = @ProbabilityRating'
END
IF @ImpactRating IS NOT NULL
BEGIN
IF @ImpactRating = 'Blank'
    SET @Where += ' AND ImpactRating = NULL OR ImpactRating = '''''
ELSE IF @ImpactRating != 'All'
    SET @Where += ' AND ImpactRating = @ImpactRating'
END
IF @CostRevenue IS NOT NULL
BEGIN
IF @CostRevenue = 'Blank'
    SET @Where += ' AND CostRevenue = NULL OR CostRevenue = '''''
ELSE IF @CostRevenue != 'All'
    SET @Where += ' AND CostRevenue = @CostRevenue'
END
IF @Status IS NOT NULL
BEGIN
IF @Status = 'Blank'
    SET @Where += ' AND Status = NULL OR Status = '''''
ELSE IF @Status != 'All'
    SET @Where += ' AND Status = @Status'
END
IF @Keywords IS NOT NULL

IF @govOwner IS NOT NULL
BEGIN
IF @govOwner = 'Blank'
    SET @Where += ' AND govOwner = NULL OR govOwner = '''''
ELSE IF @govOwner != 'All'
    SET @Where += ' AND govOwner = (SELECT FB.Alias(@govOwner))'
END
CREATE TABLE #tmp (
    ID int
    ,FeedbackType varchar(255)
    ,ImpactArea varchar(255)
    ,CreatedDate varchar(11)
    ,Customer varchar(255)
    ,IdentifiedBy varchar(255)
    ,CriticalityRating varchar(255)
    ,govOwner varchar(255)
    ,Status varchar(255)
)

SET @SQL = 'SELECT
            fb.ID
            ,FeedbackType
            ,ImpactArea
            ,CONVERT(VARCHAR(11), CreatedDate, 3) AS CreatedDate
            ,Customer
            ,IdentifiedBy
            ,CriticalityRating
            ,govOwner
            ,Status
        INTO #tmp
        FROM [FB].[Feedback] fb 
        INNER JOIN (SELECT
                        ID
                        ,MAX(Version) AS MaxVer
                    FROM FB.Feedback
                    GROUP BY ID
        ) mv ON fb.ID = mv.ID AND fb.Version = mv.MaxVer'

SET @SQLOrder = ' ORDER BY [ID] DESC'

IF @Where IS NOT NULL
    SET @Where = ' WHERE ' + (SELECT STUFF(@Where,1 , 4, '')) + ' '

IF (@Role != 'Governance Board' AND @Role != 'Admin')
    BEGIN
    IF @Where IS NOT NULL
        SET @Where += ' AND [Author] = @Alias OR [IdentifiedBy] = @Alias'
    ELSE
        SET @Where = ' WHERE [Author] = @Alias OR [IdentifiedBy] = @Alias'
    END

SET @SQL += @Where + @SQLOrder

EXECUTE SP_ExecuteSQL @SQL
    ,@Alias = @Alias
    ,@Customer = @Customer
    ,@IdentifiedBy = @IdentifiedBy
    ,@ImpactArea = @ImpactArea
    ,@Gateway = @Gateway
    ,@ProbabilityRating = @ProbabilityRating
    ,@ImpactRating = @ImpactRating
    ,@CostRevenue = @CostRevenue
    ,@Status = @Status
    ,@Keywords = @Keywords
    ,@govOwner = @govOwner

SELECT * FROM #tmp
DROP TABLE #tmp

2 个答案:

答案 0 :(得分:2)

删除行

,更改@SQL
INTO #tmp

然后在EXECUTE SP_ExecuteSQL上方添加

INSERT INTO #tmp

这会将您的EXECUTE更改为select语句,然后在此会话中将所选内容插入到临时表中。

您获得的Alias参数错误是因为您实际上错过了一点。使用参数时,sp_executesql需要@params,参数定义您正在使用的参数。 来自documentation

-- Syntax for SQL Server, Azure SQL Database, Azure SQL Data Warehouse, Parallel Data Warehouse  

sp_executesql [ @stmt = ] statement  
[   
  { , [ @params = ] N'@parameter_name data_type [ OUT | OUTPUT ][ ,...n ]' }   
     { , [ @param1 = ] 'value1' [ ,...n ] }  
] 

  

[@ params =] N'@ parameter_namedata_type [,... n]'   是一个字符串,其中包含嵌入在@stmt中的所有参数的定义。该字符串必须是Unicode常量或Unicode变量。每个参数定义都包含参数名称和数据类型。 n是占位符,表示其他参数定义。 @stmtmust中指定的每个参数都在@params中定义。如果@stmt中的Transact-SQL语句或批处理不包含参数,则不需要@params。此参数的默认值为NULL。

在您的情况下,这将如下:

DECLARE @params NVARCHAR(300) = N'@Alias VARCHAR(50), @Customer NVARCHAR(MAX), @IdentifiedBy NVARCHAR(255), @ImpactArea NVARCHAR(MAX),@Gateway NVARCHAR(255),@ProbabilityRating NVARCHAR(255), @ImpactRating NVARCHAR(255), @CostRevenue NVARCHAR(255), @Status NVARCHAR(255), @Keywords NVARCHAR(MAX), @govOwner NVARCHAR(255)';

然后按如下方式使用它:

EXECUTE SP_ExecuteSQL @SQL, @params
,@Alias = @Alias
etc, etc

因此,您必须为已执行的存储过程定义变量,还要为正在运行的动态sql定义变量,即@params

答案 1 :(得分:0)

  1. 您必须将params定义传递给sp_executesql,如下所示:
  2. DECLARE @p NVARCHAR(MAX) = 'test';
    SET @SQL = 'select @p';
    EXECUTE SP_ExecuteSQL @SQL, N'@p NVARCHAR(MAX)', @p = @p;
    
    1. 使用#tbl代替@tbl,删除,@tbl = @tbl OUTPUT。不要使用SELECT INTO声明。本地临时表在当前会话中可见,包括被调用过程中的代码和嵌套动态SQL。