そうです、最適化する必要があるこの不格好なクエリがあるので、要点を理解しながら読みやすくするために大幅に削減しました。
基本的に、最上位クエリと 3 つのサブクエリすべてで同じ「グループ化」ロジックが実行されていることがわかります。これらの列は、「内部結合」ロジックのパラメーターでもあります。問題は、これを最適化する方法がわかりませんが、同じ結果を達成するためのより簡単な方法があるに違いないことは想像できます。
関連テーブル: Invoice、InvoiceLine、ProductType
InvoiceLine は、外部キーを介して Invoice および ProductType に関連付けられています
このクエリは、1 つの請求書の合計されたinvoiceline.click を、producttype.name およびinvoiceline.origin によってグループ化され、さらにinvoice.final によって分割された他のすべての請求書の合計されたinvoiceline.click と比較することになっています。したがって、結果は次のようになります。
製品タイプ | 起源 | クリック参照請求書 | 他のすべての最終請求書をクリックします。他のすべての未確定の請求書をクリックします
クエリは機能しますが、遅すぎることを強調しておきます。このことを最適化するためのヒントはありますか?
DECLARE @startDate datetime;
DECLARE @endDate datetime;
DECLARE @refInvoiceGuid uniqueidentifier;
SET @startDate='2013-09-01 00:00:00';
SET @endDate='2013-09-30 23:59:59';
SET @refInvoiceGuid='34d03903-a2ad-49ae-bd72-e98b47cdbc52';
SELECT
ProductType.Name,
InvoiceLine.Origin,
invRef.ClicksRef,
invFinal.ClicksFinal,
invNotFinal.ClicksNotFinal
FROM InvoiceLine
INNER JOIN ProductType ON InvoiceLine.ProductType_Ref = ProductType.Id
INNER JOIN (
SELECT
ProductType.Name AS ProductName,
InvoiceLine.Origin AS Origin,
SUM(InvoiceLine.Clicks) AS ClicksRef
FROM InvoiceLine
INNER JOIN ProductType ON InvoiceLine.ProductType_Ref = ProductType.Id
INNER JOIN Invoice ON Invoice.Id = InvoiceLine.Invoice_Ref
WHERE
InvoiceLine.BillingDate >= @startDate
AND InvoiceLine.BillingDate <= @endDate
AND Invoice.Guid = @refInvoiceGuid
GROUP BY
ProductType.Name, InvoiceLine.Origin
) invRef ON ProductType.Name = invRef.ProductName AND InvoiceLine.Origin = invRef.Origin
INNER JOIN (
SELECT
ProductType.Name AS ProductName,
InvoiceLine.Origin AS Origin,
SUM(InvoiceLine.Clicks) AS ClicksFinal
FROM InvoiceLine
INNER JOIN ProductType ON InvoiceLine.ProductType_Ref = ProductType.Id
INNER JOIN Invoice ON Invoice.Id=InvoiceLine.Invoice_Ref AND Invoice.Final=1
WHERE
InvoiceLine.BillingDate >= @startDate
AND InvoiceLine.BillingDate <= @endDate
AND Invoice.Guid != @refInvoiceGuid
GROUP BY
ProductType.Name, InvoiceLine.Origin
) invFinal ON ProductType.Name = invFinal.ProductName AND InvoiceLine.Origin = invFinal.Origin
INNER JOIN (
SELECT
ProductType.Name AS ProductName,
InvoiceLine.Origin AS Origin,
SUM(InvoiceLine.Clicks) AS ClicksNotFinal
FROM InvoiceLine
INNER JOIN ProductType ON InvoiceLine.ProductType_Ref = ProductType.Id
INNER JOIN Invoice ON Invoice.Id=InvoiceLine.Invoice_Ref AND Invoice.Final=0
WHERE
InvoiceLine.BillingDate >= @startDate
AND InvoiceLine.BillingDate <= @endDate
AND Invoice.Guid != @refInvoiceGuid
GROUP BY
ProductType.Name, InvoiceLine.Origin
) invNotFinal ON ProductType.Name = invNotFinal.ProductName AND InvoiceLine.Origin = invNotFinal.Origin
WHERE
InvoiceLine.BillingDate >= @startDate
AND InvoiceLine.BillingDate <= @endDate
GROUP BY
ProductType.Name,
InvoiceLine.Origin,
invRef.ClicksRef,
invFinal.ClicksFinal,
invNotFinal.ClicksNotFinal
更新 1
インデックスを追加しました:
CREATE NONCLUSTERED INDEX [IX_ProductOrigin] ON InvoiceLine (Invoice_Ref,BillingDate) INCLUDE (Origin,Clicks,ProductType_Ref);
そして、クエリをよりコンパクトに書き直しました(明らかに、これは同等のパフォーマンスを持っています):
SELECT
ProductType.Name,
InvoiceLine.Origin,
SUM(CASE WHEN Invoice.Guid = @refInvoiceGuid THEN InvoiceLine.Clicks ELSE 0 END) AS ClicksRef,
SUM(CASE WHEN Invoice.Guid <> @refInvoiceGuid AND Invoice.Final = 1 THEN InvoiceLine.Clicks ELSE 0 END) AS ClicksFinal,
SUM(CASE WHEN Invoice.Guid <> @refInvoiceGuid AND Invoice.Final = 0 THEN InvoiceLine.Clicks ELSE 0 END) AS ClicksNotFinal
FROM InvoiceLine
INNER JOIN ProductType ON InvoiceLine.ProductType_Ref = ProductType.Id
INNER JOIN Invoice ON Invoice.id = InvoiceLine.Invoice_Ref
WHERE InvoiceLine.BillingDate >= @startDate AND InvoiceLine.BillingDate <= @endDate
GROUP BY ProductType.Name, InvoiceLine.Origin
速度が向上したように見え、確かに可読性が向上しましたが、実際には実行に 3 分かかります。統計と実行計画は次のとおりです。
統計を正しく理解していれば、インデックスのシークに非常に長い時間がかかっているだけですか? これを改善する方法についてのアイデアはありますか?それとも、データが多すぎるところまで来ているのでしょうか?
インデックスの統計は次のとおりです。
そしてテーブル:
「set statistics io on」を実行した結果:
(170 row(s) affected)
Table 'ProductType'. Scan count 0, logical reads 340, 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 'InvoiceLine'. Scan count 2741, logical reads 37444, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Invoice'. Scan count 1, logical reads 115, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
更新 2
インデックス列の順序を逆にした後:
CREATE NONCLUSTERED INDEX [IX_ProductOrigin] ON InvoiceLine (BillingDate,Invoice_Ref) INCLUDE (Origin,Clicks,ProductType_Ref);
(170 row(s) affected)
Table 'ProductType'. Scan count 0, logical reads 340, 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 'InvoiceLine'. Scan count 1, logical reads 28371, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Invoice'. Scan count 1, logical reads 115, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
インデックス シーク統計を含む実行プラン:
これにより、パフォーマンスが大幅に向上しました (170 秒から 15 秒になりました)。これまでご協力いただきありがとうございました。他の提案はありますか?