聚合查詢的慢速執行聚合
我正在尋找截至特定時間點有負餘額的所有賬戶的總餘額。我已經得到了下面的程式碼,它可以工作,但是效果很糟糕。
事務表包含 2000 萬行。下面的查詢大約需要 30 秒,這還不錯,但第二部分是我需要在數據庫啟動後的每個月的第一天重複 @report_date,這會將執行時間增加到約 30 分鐘。
這是在 MSSQL2008 上,我在執行計劃上沒有收到任何缺少索引的警告,但我懷疑我的問題仍然出在索引上,所以我故意將它們放在此處。
CREATE TABLE transactions( transaction_id int, account_id int, department_id int, location_id int, post_date date, amount money ); SELECT t2.department_id, t2.location_id, SUM(t2.credit_balances) FROM ( SELECT t1.department_id, t1.location_id, t1.account_id, SUM(t1.amount) as credit_balances FROM transactions t1 WHERE t1.post_date < @report_date GROUP BY t1.department_id, t1.location_id, t1.account_id HAVING SUM(t1.amount) < 0 ) t2 GROUP BY t2.department_id, t2.location_id;
該表包含 38 個不同的
department_id,location_id
組合,以及 450 萬個不同的department_id,location_id,account_id
.
當您重複此查詢數月時,您將不斷重新聚合相同的行。
例如,第一個月的行將始終由
t1.post_date < @report_date
標準帶回,因此每個月都會重新處理。為了避免這種情況,我可能會考慮從一開始就以迭代的方式一個月一次地完成它。根據歷史數據的波動性,我還可以考慮將預先計算的結果儲存在數據庫中,而不是每個月重新計算這些結果。
要在執行時計算此值,您可以創建一個具有以下結構的臨時表。
CREATE TABLE #balance ( department_id INT NOT NULL, location_id INT NOT NULL, account_id INT NOT NULL, balance_to_date MONEY NOT NULL, PRIMARY KEY (department_id, location_id, account_id) );
transactions
您還可以考慮在您的表上添加以下索引ALTER TABLE transactions ADD post_date_year_month AS (10000 * YEAR(post_date) + MONTH(post_date)) CREATE INDEX ix ON transactions(post_date_year_month, department_id, location_id, account_id) INCLUDE (amount)
然後一次提取一個月
transactions
並合併到#balance
(匹配時增加,不匹配時插入)。前導
post_date_year_month
列意味著只要您編寫查詢 sargably,就可以有效地完成每個月的提取,並且一個月的提取行將通過在沒有排序的情況下department_id, location_id, account_id
進行合併連接#balance
來排序。雖然這可能有利於此特定查詢,但您需要根據您的整體工作負載評估此索引的實用性。
然後計算
department_id, location_id
總數#balance
(可以利用 PK 順序來避免排序)並將它們儲存在某個地方並移動到下個月。(或者可能代替
#balance
您使用“臨時”永久表balance
並在其上創建索引視圖以避免單獨的顯式聚合步驟,並在繼續之前直接複製值)