採用された各営業担当者の集計データを取得して、Salesname
、saleamount
、およびsaleshippingamount
を表示します。 2 UPDATE
CTEステートメントを使用して、各従業員のマスターデータを格納するテーブルを更新するようにクエリを設定しています。合計を表示する必要があるため、更新しています。
構文が完了するまでに約10分かかり、2つのテーブルの間に処理する行が約60,000行あります。 CTEはこれについて間違った方法ですか?
以下はサンプルDDLです。従業員名はHRテーブルから入力されますが、これについてはDDLを表示せず、挿入ステートメントのみを示しました。
Create Table #FinalData
(
employee varchar(400)
,tr float
,tf float
)
Create Table #TR
(
employee varchar(400)
,saleamount float
,saledate date
)
Create Table #TF
(
employee varchar(400)
,saleshippingamt float
,saledate date
)
INSERT INTO #TR (employee, saleamount, saledate) VALUES
('Employee 1', '12.63', '2017-01-01'), ('Employee 1', '15.00' ,'2017-01-02')
,('Employee 2', '14.00', '2017-01-03'), ('Employee 2' ,'12.00', '2017-01-03')
,('Employee 3', '16.00', '2017-01-03'), ('Employee 3', '13.00', '2017-01-04')
INSERT INTO #TF (employee, saleshippingamt, saledate) VALUES
('Employee 1', '5.00', '2017-01-01'), ('Employee 1', '6.00' ,'2017-01-02')
,('Employee 2', '5.50', '2017-01-03'), ('Employee 2' ,'5.00', '2017-01-03')
,('Employee 3', '6.00', '2017-01-03'), ('Employee 3', '6.00', '2017-01-04')
INSERT INTO #FINALDATA (employee) Values
('Employee 1'), ('Employee 2'), ('Employee 3')
;With TR As
(
SELECT
employee
,SUM(saleamount) Totals
FROM #TR
WHERE saledate BETWEEN '2017-01-01' AND '2017-01-25'
GROUP BY employee
)
UPDATE t
SET tr = t2.TotalCount
FROM #FinalData t
INNER JOIN (Select employee, SUM(Totals) TotalCount
FROM TR
GROUP BY employee) As t2
ON t.employee = t2.employee
;With TF As
(
SELECT
employee
,SUM(saleshippingamt) Totals
FROM #TF
WHERE saledate BETWEEN '2017-01-01' AND '2017-01-25'
GROUP BY employee
)
UPDATE t
SET tf = t2.TotalCount
FROM #FinalData t
INNER JOIN (Select employee, SUM(Totals) TotalCount
FROM TF
GROUP BY employee) As t2
ON t.employee = t2.employee
Select * FROM #FinalData
そして、これが私の 実行計画 です。
このクエリを最適化するために必要なオプションは何ですか?
ようやくテストとクエリの操作ができるようになり、より具体的な提案ができるようになりました。サンプルにはAdventureWorksを使用したので、実際に使用するデータがいくつかありました。
これにより、最小限の労力で最高のパフォーマンスが向上します。 3つのインデックスを追加するだけで、サンプルセットのクエリ実行が40%増加しました。
テーブル#TR
および#TF
。 saledate
、employee
にインデックスを追加し、金額を含めます。
CREATE INDEX IDXNC_TFSaleDateEmployee ON #TF (saledate, employee) INCLUDE (saleshippingamt)
CREATE INDEX IDXNC_TRSaleDateEmployee ON #TR (saledate, employee) INCLUDE (saleamount)
#FinalData
の場合、employee
フィールドにクラスター化インデックスが必要です。
CREATE CLUSTERED INDEX IDXC_FinalDataEmployee ON #FinalData (employee)
クエリでは微調整を使用できます。一時テーブルをまとめて削除することもできます。
このクエリも少し高速に実行されますが、インデックスを使用するとパフォーマンスが最も向上します。
;WITH CTE_Employee AS
(
SELECT DISTINCT employee
FROM (
SELECT employee
FROM #TR
UNION ALL
SELECT employee
FROM #TF
) AS D
)
, CTE_TR AS
(
SELECT employee
, SUM(saleamount) AS TotalDue
FROM #TR
WHERE saledate BETWEEN '2017-01-01' AND '2017-01-25'
GROUP BY employee
)
, CTE_TF AS
(
SELECT employee
, SUM(saleamount) AS TotalDue
FROM #TR
WHERE saledate BETWEEN '2017-01-01' AND '2017-01-25'
GROUP BY employee
)
SELECT S.employee
, TR.TotalDue
, TF.TotalDue
FROM CTE_Employee AS S
LEFT OUTER JOIN CTE_TR AS TR ON TR.employee = S.employee
LEFT OUTER JOIN CTE_TF AS TF ON TF.employee = S.employee
CTEには好きな名前を付けることができますが、CTEが表すテーブルと同じ名前を付けると少し混乱します。 CTE_*
プレフィックスを付けたいのですが、これはオプションです。
ここで行ったように、CTEをチェーンすることもできます。
元のクエリでは、#FinalData
のtotalcount
の合計を取得するときに、2つの集計演算を実行していました。これも注意が必要です。不要であることがわかりますが、SQLは並べ替えと合計の操作を複数回実行します。副選択を行う代わりに、CTEテーブルを結合するだけで済みます。
あなたが持っていた
UPDATE t
SET tf = t2.TotalCount
FROM #FinalData t
INNER JOIN (Select employee, SUM(Totals) TotalCount
FROM TF
GROUP BY employee) As t2
ON t.employee = t2.employee
する必要があります
UPDATE t
SET tf = t2.TotalCount
FROM #FinalData t
INNER JOIN TF As t2 ON t.employee = t2.employee
ダミーデータをポストしますが、常に実際のテーブル構造をポストします。これは、実際のビューを取得するのに役立ちます。たとえば、emloyee varchar(400)を使用した#FinalDataは現実的ではないと思います。
IMHO、varchar(400)でCIまたは任意のインデックスを作成することは非常に悪いため、インデックスのメリットはありません。
varchar(400)は、インデックスのみをカバーするのに最適です。
したがって、実際にそのような状況が実際にある場合は、可能な限り最適化されたクエリを作成することに集中します。より速い結果を得るためにページングなどを適用する場合があります。
そのような状況ではインデックスを作成しません。
したがって、あなたが投稿したデータと構造に従って、ハードコードの代わりに変数を使用します。
内部結合が機能しない場合は、左結合に進みます
select fd.employee
,tr.Totals
,tf.shippingamtTotals
from
#FinalData FD
inner JOIN
(
SELECT
employee
,SUM(saleamount) Totals
FROM #TR
WHERE saledate BETWEEN '2017-01-01' AND '2017-01-25'
GROUP BY employee
)tr
on fd.employee=tr.employee
inner join
(
SELECT
employee
,SUM(saleshippingamt) shippingamtTotals
FROM #TF
WHERE saledate BETWEEN '2017-01-01' AND '2017-01-25'
GROUP BY employee
)tf
on fd.employee=tf.employee