2011-05-05 97 views
3

我對許多任務使用了動態SQL,並不斷遇到同樣的問題:打印Dynamic T-SQL語句中使用的變量的值。打印動態參數值

EG:

Declare @SQL nvarchar(max), @Params nvarchar(max), @DebugMode bit, @Foobar int 
select @DebugMode=1,@Foobar=364556423 

set @SQL='Select @Foobar' 
set @Params=N'@Foobar int' 

if @DebugMode=1 print @SQL 
exec sp_executeSQL @SQL,@Params 
    ,@[email protected] 

上述代碼的打印結果僅僅是 「選擇@Foobar」。有什麼方法可以動態地打印正在執行的sql的變量名稱的值&?或者在執行打印時,將參數替換爲實際值,以便SQL可重新運行?

我已經開始創建一個或兩個函數來完成類似的事情,但數據類型轉換,模式匹配截斷問題和非動態解決方案。我很好奇其他開發人員如何解決此問題,而無需手動手動打印每個變量。

+0

使用分割功能只是爲了讓您的使用場景中更好的畫面,這是你的東西進行性能優化,故障排除問題,審計,其他一些目的做更多的時候? – 2011-05-06 21:04:53

+0

@Phil Helmer,通常用於問題故障排除,但偶爾會在執行一些更令人困惑的動態查詢時進行性能調整。 – 2011-05-09 19:50:12

回答

3

我不相信評估的說法是可用的,這意味着你的例子查詢「選擇@FooBar」是永遠不會在任何地方持續爲即使在事件探查器跟蹤「選擇364556243」

,你會看到聲明命中緩存爲「 (@Foobar int)select @foobar'

這很有意義,因爲使用sp_executesql的一大好處是它能夠以可靠的形式緩存語句而不用評估變量,否則如果它替換變量並執行聲明我們只會看到執行計劃膨脹。

更新:這裏有一個一步在正確的方向

所有這一切都可以被清理,幷包裹在一個不錯的功能,具有輸入(@Statement,@ParamDef,@ParamVal)和將返回「準備好的」聲明。我會將其中的一部分作爲練習留給你,但是當你改進時請回復!

從這裏link

set nocount on; 

declare @Statement varchar(100), -- the raw sql statement 
     @ParamDef varchar(100), -- the raw param definition 
     @ParamVal xml    -- the ParamName -to- ParamValue mapping as xml 


-- the internal params: 
declare @YakId int, 
     @Date datetime 
select @YakId = 99, 
     @Date = getdate(); 


select @Statement = 'Select * from dbo.Yak where YakId = @YakId and CreatedOn > @Date;', 
     @ParamDef = '@YakId int, @Date datetime'; 

-- you need to construct this xml manually... maybe use a table var to clean this up 
set @ParamVal = ( select * 
        from ( select '@YakId', cast(@YakId as varchar(max)) union all 
           select '@Date', cast(@Date as varchar(max)) 
          ) d (Name, Val) 
        for xml path('Parameter'), root('root') 
       ) 

-- do the work 
declare @pStage table (pName varchar(100), pType varchar(25), pVal varchar(100)); 
;with 
    c_p (p) 
    as ( select replace(ltrim(rtrim(s)), ' ', '.') 
      from dbo.Split(',', @ParamDef)d 
     ), 
    c_s (pName, pType) 
    as ( select parsename(p, 2), parsename(p, 1) 
      from c_p 
     ), 
    c_v (pName, pVal) 
    as ( select p.n.value('Name[1]', 'varchar(100)'), 
        p.n.value('Val[1]', 'varchar(100)') 
      from @ParamVal.nodes('root/Parameter')p(n) 
     ) 
insert into @pStage 
    select s.pName, s.pType, case when s.pType = 'datetime' then quotename(v.pVal, '''') else v.pVal end -- expand this case to deal with other types 
    from c_s s 
    join c_v v on 
      s.pName = v.pName 

-- replace pName with pValue in statement 
select @Statement = replace(@Statement, pName, isnull(pVal, 'null'))      
from @pStage 
where charindex(pName, @Statement) > 0; 

print @Statement; 
+0

我覺得它並沒有在任何地方持續存在,但希望有人在保持適當的數據類型的同時,動態地用替換變量的值進行了一些神奇的解決方案。 – 2011-05-06 16:57:25

+0

好吧,我不會稱之爲'神奇',但它是一個開始:) – 2011-05-06 19:18:06

+0

我喜歡它。你是爲了這個答案而寫出來的,還是你已經在循環使用的東西?這讓我更加好奇地聽到其他人回答關於@Brent D的「其他人如何做這件事?」的問題。感謝你的分享。 – 2011-05-06 21:01:30

2

上的大多數人是如何做到這一點的話題,我只會說給我做的:

  • 創建一個測試腳本將運行使用範圍廣泛的有效和無效輸入的過程。如果參數是一個整數,我會發送'4'(而不是4),但我只會嘗試1個古怪的字符串值,如'agd'。
  • 針對代表性大小和數據值分佈的數據集運行我正在執行的操作。使用您最喜歡的數據生成工具(市場上有好幾款很好的數據生成工具)來加快速度。
  • 我通常在更臨時的基礎上進行調試,所以從SSMS結果窗口收集結果儘可能我需要。

我能想到的最好的方法是捕捉查詢,因爲它使用SQL跟蹤通過線路。如果您在查詢字符串中添加了唯一的內容(作爲註釋),那麼在跟蹤中爲其應用過濾器非常簡單,這樣您就不會捕獲超過需要的內容。

但是,它不是所有的桃子&奶油。

這隻適用於開發環境,也許質量保證,這取決於你的商店是多麼僵化。

如果查詢需要很長時間才能運行,那麼可以通過在查詢字符串中添加「TOP 1」,「WHERE 1 = 2」或類似的限制子句來緩解這種情況,如果@DebugMode = 1。最終可能會等待一段時間才能完成每次。

對於查詢字符串只能用於調試模式而無法添加某些內容的長查詢,您可以在StmtStarted事件中捕獲命令文本,然後一旦有命令就立即取消該查詢。

如果查詢是INSERT/UPDATE/DELETE,如果@DebugMode = 1並且您不希望發生更改,則需要強制回滾。如果您目前沒有使用明確的交易,那麼這會造成額外的開銷。

如果你走這條路,你可以通過一些自動化來讓生活更輕鬆。您可以爲跟蹤創建和開始/停止操作創建模板。您可以將結果記錄到文件或表格中,然後以編程方式處理命令文本。

+0

這肯定只適用於@ DebugMode = 1時,它只會在Dev或QA中,或者在Production上的回滾事務中手動運行查詢。我相信SQL跟蹤將只顯示原始查詢參數不被替換爲值 - 例如:「select @Foobar」,而不是「選擇'Foobar文本'」 – 2011-05-06 16:59:50