2013-06-26 126 views
2

我有一個表(可以稱之爲audit),看起來像這樣:選擇從範圍內的最低日期和排除另一個範圍

+--------------------------------------------------------------------------+ 
| id | recordId | status | mdate     | type | relatedId  | 
+--------------------------------------------------------------------------+ 
| 1 | 3006  | A  | 2013-04-03 23:59:01.275 | type1 | 1    | 
| 2 | 3025  | B  | 2013-04-04 00:00:02.134 | type1 | 1    | 
| 3 | 4578  | A  | 2013-04-04 00:04:30.033 | type2 | 1    | 
| 4 | 7940  | C  | 2013-04-04 00:04:32.683 | type1 | <NULL>  | 
| 5 | 3006  | D  | 2013-04-04 00:04:32.683 | type1 | <NULL>  | 
| 6 | 4822  | E  | 2013-04-04 00:04:32.683 | type2 | <NULL>  | 
| 7 | 3006  | A  | 2013-04-04 00:06:54.033 | type1 | 2    | 
| 8 | 3025  | C  | 2013-04-04 00:06:54.033 | type1 | 2    | 

...和對數百萬行的。而另一張表,我們將撥打related

+-------------+ 
| id | source | 
+-------------+ 
| 1 | src_X | 
| 2 | src_Y | 
| 3 | src_Z | 
| 4 | src_X | 
| 5 | src_X | 

......並開啓數十萬行。

這兩個表格上的列數多於這些列,但這是我們需要描述問題的全部內容。列relatedId加入related表。 recordId也加入到另一個表中,並且audit中將有多個條目與recordId相同。

我試圖創建將產生以下輸出的查詢:

+-----------------+ 
| source | count | 
+-----------------+ 
| src_X | 1643 | 
| src_Y | 255 | 
| NULL | 729 | 
+-----------------+ 

的計數的記錄中audit數量已經給定type(如"type1"),是一個集內的狀態(例如,"A", "B", "C"),然後將其外部加入related並按source分組。

美中不足的是,我只希望包括audit是在特定日期範圍內的內記錄,而我也只是想從audit加入到related在該範圍內,最早的條目爲每個recordId。此外,我想忽略任何與typestatus條件匹配的記錄,但是具有相同的recordId的條目比日期範圍更早。

所以,從上面的例子中的數據闡明:可以說,我想的類型的type1"A", "B", "C"2013-04-042013-04-05日期範圍的狀態值。第2行和第4行將包含在計數中。第3行被排除,因爲它有不正確的type。由於狀態不正確,第5行被排除。第6行被排除,因爲狀態和類型都不正確。排除第1行,因爲它在日期範圍之外。第7行也被排除,因爲還有一行(第1行)與狀態和類型標準相匹配,並且具有相同的recordId,該行比日期範圍的開始時間更早。第8行被排除,因爲第8行和第2行具有相同的recordId並符合標準,但我們只計算範圍內兩個最舊的記錄。

換句話說,我想只計算給定recordId的條目第一次出現在表中並且在目標日期範圍內。

我們已經想出了以下內容:

WITH data (recordId, id) AS (
    SELECT a.recordId, MIN(a.id) 
    FROM audit a 
    WHERE a.status in ('A','B','C') 
     AND type = 'type1' 
    GROUP BY a.recordId 
) 
SELECT r.source, COUNT(*) 
FROM data d 
    JOIN audit a ON d.id = a.id 
    LEFT JOIN related r ON a.relatedId = r.id 
WHERE a.mdate >= '2013-04-04 00:00:00.000' 
    and a.mdate < '2013-04-05 00:00:00.000' 
GROUP BY r.source 

這將MSSQL Server 2008上運行,目前依賴於一個事實,即審計表ID是自動生成的。由於id是在插入記錄時生成的,並且mdate也是插入時間戳,並且記錄一旦插入就不會更新,所以我認爲這是正確的。該查詢似乎給出了有限的一組測試數據的正確輸出,但我希望得到第二個意見。

  • 此查詢是否正常?
  • 其性能可以提高嗎?
+1

計算表格表達式中的日期範圍可能會提高性能。 –

+0

好點。將'AND a.mdate <'2013-04-05 00:00:00.000''添加到計算表中將有助於限制它返回的記錄數。 –

+0

爲了提高查詢性能,請考慮索引。在WHERE Clause Fields,Join Fields上使用索引,然後再次測試性能。 – 2013-06-26 11:58:25

回答

4

您可以使用ROW_NUMBER()函數根據RecordId和mDate對記錄進行排名,然後將結果限制爲第一次出現在指定日期之間的位置。

WITH data AS 
( SELECT a.relatedId, a.mdate, rn = ROW_NUMBER() OVER(PARTITION BY a.RecordId ORDER BY a.mdate) 
    FROM audit a 
    WHERE a.status in ('A','B','C') 
    AND  type = 'type1' 
) 
SELECT r.source, [Count] = COUNT(*) 
FROM data d 
     LEFT JOIN related r 
      ON d.relatedId = r.id 
WHERE d.rn = 1 
AND  d.mdate >= '2013-04-04 00:00:00.000' 
AND  d.mdate < '2013-04-05 00:00:00.000' 
GROUP BY r.source; 

我不確定這會比目前的解決方案執行得更好,但會解決依賴按時間順序插入的問題。如果按時間順序插入不成問題,則可以將ROW_NUMBER()函數中的ORDER BY更改爲使用ID,因爲對集羣密鑰的排序會更快。

從外部查看性能調整非常困難,爲了甚至猜測它,我們需要查看相關表上的索引以及查詢的執行計劃。然後,您可以識別瓶頸,以及哪些索引可以提高性能。

This SQL Fiddle顯示了兩個查詢(我和你)有相同的結果結束了,但是當你看看IO統計數據,你可以看到你查詢你:

(2 row(s) affected) 
Table 'Related'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Audit'. Scan count 2, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

使用ROW_NUMBER()你得到:

(2 row(s) affected) 
Table 'Related'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Audit'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

關鍵因素是一個較少的邏輯閱讀。快速查看執行計劃顯示,ROW_NUMBER()解決方案的分支少一個,估計爲批處理成本的37%,而您的解決方案爲63%,因此對於這一小部分數據,它似乎是一個性能改進。

enter image description here

然而也只有這麼多,我可以從數據的這樣一個小樣本出​​來,一些解決方案不能很好地擴展,正如我已經說過,將取決於你的數據的大小和分佈狀態。我的建議是嘗試不同的解決方案,通過檢查IO統計數據和執行計劃來找到瓶頸。

例如,在看的CTE這佔了我的查詢查詢成本的50%執行計劃:

enter image description here

通過添加該指數:

CREATE INDEX IX_Audit_ALL ON Audit (recordId, MDate, RelatedID, status, type) 

我能夠將其降低到查詢成本的18%。

enter image description here

然而,在不知道我越不能明確說這個實用性將指標(一)幫助這個查詢與您的數據和(b),它不會導致其他問題與您的數據庫通過減慢插入/更新。

+0

感謝您的全面回答! –