2016-08-25 76 views
1

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

我省略了报表生成简洁的参数(这些工作,如果我生成整个字符串并运行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

变化@SQL通过删除线

INTO #tmp 

然后上面EXECUTE SP_ExecuteSQL添加一行

INSERT INTO #tmp 

这将改变你的EXECUTEselect语句,然后插入什么是选择到您的临时表,全部在这个会话中。

您得到的关于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中定义。如果Transact-SQL语句或@stmt中的批处理不包含参数,则不需要@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 

所以,你必须定义存储procedue你的变量,你已经完成了,还为你正在运行的动态sql,这是@params

+0

伟大的工作谢谢。我现在唯一的问题是使用'@Alias'参数,如果变量为空,执行'sp_executesql'的最佳实践是什么? – Tom

+0

答案用该位更新。 – BeanFrog

+1

仅供将来参考'DECLARE @params NVARCHAR(300)'太短了。我更新了这个'NVARCHAR(400)',它工作正常 – Tom

0
  1. 你必须通过PARAMS定义sp_executesql,如下所示:
DECLARE @p NVARCHAR(MAX) = 'test'; 
SET @SQL = 'select @p'; 
EXECUTE SP_ExecuteSQL @SQL, N'@p NVARCHAR(MAX)', @p = @p; 
  • 使用#tbl代替@tbl,除去,@tbl = @tbl OUTPUT。请勿使用SELECT INTO声明。本地临时表在当前会话中可见,包括调用过程中的代码和嵌套的动态SQL。
  • +0

    我已经宣布他们在顶部,所以我不需要重新声明我认为 – Tom

    +0

    @Tom,尝试我的示例没有params定义传递,它给错误 –