2016-08-25 127 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定義傳遞,它給錯誤 –