2012-08-07 84 views
3

我有以下代碼重複運行存儲過程。當我逐字運行SQL語句時,它工作得很好,所以我創建了一個存儲過程,它封裝了我正在做的事情。巨大的減速C#存儲過程調用,參數嗅探/優化問題?

foreach (string worker in workers) 
{ 
    _gzClasses.ExecuteCommand("EXEC dbo.Session_Aggregate @workerId = {0}, @timeThresh = {1}", worker, SecondThreshold); 
    Console.WriteLine("Inserted sessions for {0}", worker); 
} 

然後,我想知道每個呼叫有多少行產生,所以我改變了SP略微返回@@rowcount作爲輸出參數。我can't use the DataContext to execute commands with output parameters,所以我不得不改變內部的上述代碼迴路以下幾點:

using (var cn = new SqlConnection(CnStr)) 
{ 
    cn.Open(); 
    using (var cmd = new SqlCommand("Session_Aggregate", 
     cn) {CommandTimeout = 300}) 
    {       
     cmd.CommandType = CommandType.StoredProcedure;       

     cmd.Parameters.AddWithValue("@workerId", worker);       
     cmd.Parameters.AddWithValue("@timeThresh", SecondThreshold);       

     SqlParameter sessions = cmd.Parameters.Add("@sessions", SqlDbType.Int); 
     sessions.Direction = ParameterDirection.Output; 

     cmd.ExecuteNonQuery(); 

     Console.WriteLine("Inserted {1} sessions for {0}", worker, sessions.Value); 
    } 
} 

這工作,但它運行要比其他查詢慢。我認爲這可能是參數嗅探的一種情況,所以我將其更改爲CommandType.Text並使用字符串EXEC Session_Aggregate ... WITH RECOMPILE。但在這種情況下,我一直收到出錯參數@session未定義的錯誤。在任何情況下,即使SQL命令在1秒內在SSMS中運行<,現在查詢幾乎不運行。

這裏是存儲過程,以防萬一任何人都可以幫忙弄清楚發生了什麼,或者可以找出加快速度的方法。我也會指導如何正確描述這裏發生的事情。與CommandType.StoredProcedure我什至不能看到由VS發送到SQL的實際命令。

PROCEDURE [dbo].[Session_Aggregate] 
    -- Add the parameters for the stored procedure here 
    @workerId varchar(64) = 0, 
    @timeThresh dateTime = '13 July 2007 11:27:46' 
    @sessions INT OUTPUT 
AS 
BEGIN 
    -- SET NOCOUNT ON added to prevent extra result sets from 
    -- interfering with SELECT statements. 
    SET NOCOUNT ON; 

    -- Insert statements for procedure here 
    INSERT INTO e_activeSessions 
    SELECT * 
    FROM (
     SELECT workerId, startTime, COUNT(*) as totalTasks, MAX(timeInSession) as totalTime, 
     MIN(dwellTime) as minDwell, MAX(dwellTime) as maxDwell, AVG(dwellTime) as avgDwell, STDEV(dwellTime) as stdevDwell, 
     SUM(CAST(wrong80 as INT)) + SUM(CAST(correct80 as INT)) as total80, SUM(CAST(correct80 as INT)) as correct80, 
     SUM(CAST(correct80 as FLOAT))/NULLIF(SUM(CAST(wrong80 as INT)) + SUM(CAST(correct80 as INT)), 0) as percent80 
     FROM (
      SELECT *, (SELECT MAX(timeStamp) 
       FROM workerLog w where dwellTime is null AND timeInSession = 0 AND workerId = @workerId AND w.timeStamp <= workerLog.timeStamp 
        AND w.timeStamp >= @timeThresh) as startTime 
      FROM workerLog where workerId = @workerId) t 
    GROUP BY startTime, workerId) f 
    WHERE startTime is NOT NULL AND f.totalTasks > 1 AND totalTime > 0; 

    SET @sessions = @@ROWCOUNT; 
END 

編輯:不管原始查詢的執行計劃的,它是由創建臨時表加速顯著。我以爲SQL會通過分析查詢來完成這項工作,但我可能是錯的。 另外,我發現了OPTIMIZE FOR UNKNOWN提示,它在新版本的SQL Server中減輕了執行計劃針對大量不同數據大小時參數嗅探的影響。

PROCEDURE [dbo].[Session_Aggregate] 
    -- Add the parameters for the stored procedure here 
    @workerId varchar(64) = 0, 
    @timeThresh dateTime = '13 July 2007 11:27:46', 
    @sessions INT OUTPUT 
AS 
BEGIN 
    -- SET NOCOUNT ON added to prevent extra result sets from 
    -- interfering with SELECT statements. 
    SET NOCOUNT ON; 

    -- Insert statements for procedure here 

    CREATE TABLE #startTimes 
    (
     startTime DATETIME 
    ); 

    CREATE INDEX Idx_startTime ON #startTimes(startTime); 

    INSERT INTO #startTimes 
    SELECT timeStamp FROM workerLog 
    WHERE dwellTime is null AND timeInSession = 0 
    AND workerId = @workerId AND timeStamp >= @timeThresh; 

    INSERT INTO e_activeSessions 
    SELECT * 
    FROM (
     SELECT workerId, startTime, COUNT(*) as totalTasks, MAX(timeInSession) as totalTime, 
     MIN(dwellTime) as minDwell, MAX(dwellTime) as maxDwell, AVG(dwellTime) as avgDwell, STDEV(dwellTime) as stdevDwell, 
     SUM(CAST(wrong80 as INT)) + SUM(CAST(correct80 as INT)) as total80, SUM(CAST(correct80 as INT)) as correct80, 
     SUM(CAST(correct80 as FLOAT))/NULLIF(SUM(CAST(wrong80 as INT)) + SUM(CAST(correct80 as INT)), 0) as percent80 
     FROM (
      SELECT *, (SELECT MAX(startTime) FROM #startTimes where startTime <= workerLog.timeStamp) as startTime 
      FROM workerLog where workerId = @workerId) t 
    GROUP BY startTime, workerId) f 
    WHERE startTime is NOT NULL AND f.totalTasks > 1 AND totalTime > 0 
    OPTION (OPTIMIZE FOR UNKNOWN); 

    SET @sessions = @@ROWCOUNT;  
END; 

附加簡化:拖動SP到你的DBML文件,你可以做到以下幾點:

foreach (string worker in workers) 
{ 
    int? rows = 0; 
    _gzClasses.Session_Aggregate(worker, SecondThreshold, ref rows); 

    Console.WriteLine("Inserted {1} sessions for {0}", worker, rows); 
} 

回答

1

火起來SQLServerProfiler,並且可以給你單查詢和之間的區別你現在正在運行它的方式。

http://www.techrepublic.com/article/step-by-step-an-introduction-to-sql-server-profiler/5054787

但更重要的是你應該看看,你可以通過查詢瓷磚和選擇顯示的執行計劃,SSMS打開查詢執行計劃。

http://www.mssqltips.com/sqlservertip/1856/sql-server-query-execution-plans-in-sql-server-management-studio/

如果你是真正的新SSMS我可能會看一對夫婦對我提供什麼上面的文章,但查詢執行計劃真的會告訴你在哪裏查詢滯後。 (基本的經驗法則是你不希望全表掃描發生,你希望它做的是尋找,這意味着你希望它在索引和/或主鍵上搜索)我不是dba,但那是在調試查詢時你可能想要採取的路線。

我不太確定這是您的查詢後,雖然審查,因爲它看起來很簡單。它可能與你調用它的次數有關。您可能想要找出一種方法將所有工作人員數據傳遞到查詢中,以便您只運行一次查詢本身,然後運行它。workers.count次......HTH

+0

現在,我只想讓一個查詢快速運行,然後我可以考慮一次爲所有的工作人員執行此操作。對我來說,當我調用它時,它可以在<1秒內運行,然後在5分鐘後超時以執行不同的執行。一些執行計劃是否可以真正進行掃描,而其他執行計劃則不是? SQL服務器分析器文章真的很老......我究竟在哪裏找到應用程序來運行它? – 2012-08-07 23:51:43

+0

Sql server profiler應該位於您正在使用的SQL Server版本的工具文件夾中。上面的開放和關閉建議是降低查詢運行時間的好主意,但它不應該導致您描述的問題。您最好的選擇是使用查詢分析器。因爲它會給你一個關於爲什麼一個查詢的運行時間比另一個長的想法。它還將幫助您查明它是否是您的查詢或您的代碼。 – 2012-08-08 03:05:12

+0

您可能想要考慮的另一件事是您的兩個不同查詢之間的數據量差異。查看查詢1的數據列表,並將其與查詢2進行比較。有可能是查詢2正在拉動一個度量標準對齊更多數據。我之前遇到過這樣的情況,在那裏我有一個有數百萬人的記錄,或者是記錄中只有10,000個記錄的記錄。還有別的想法。 – 2012-08-08 03:14:07