2

緊急ではなく、注文ステータス= 1(出荷済み)のすべての注文をカウントしたいと思います。

これは、最適化するための非常に単純なクエリである必要があります。Ordersテーブルに単純なフィルター処理されたインデックスを配置して、このクエリをカバーし、定数時間/ O(1)操作にします。ただし、クエリプランを見ると、意味のないインデックススキャンを使用しているように見えます。理想的には、このクエリはインデックス内のアイテムの数を返すだけである必要があります。

表は次のようになります(本質を理解するために簡略化されています)。

CREATE TABLE [dbo].[Orders](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [IsUrgent] [bit] NOT NULL,
    [Status] [tinyint] NOT NULL
 CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED ( [Id] ASC )

このフィルタリングされたインデックスを作成しました:

CREATE INDEX IX_Orders_ShippedNonUrgent ON Orders(Id) WHERE IsUrgent = 0 AND Status = 1;

さて、私がこのクエリを行うとき:

SELECT COUNT(*) FROM Orders WHERE IsUrgent = 0 AND Status = 1

クエリプランはIX_Orders_ShippedNonUrgentを使用しているようですが、インデックススキャンを実行し、Ordersの約150,000行で約200回の読み取りを実行しています。

フィルタリングされたインデックスが最新の状態に保たれていると仮定して、このクエリを常に一定時間で実行することは可能ですか?理想的には、インデックスのサイズを取得するために1回の読み取りのみを実行する必要があります。

このようにフィルタリングされていないインデックスに切り替えると、次のようになります。

CREATE INDEX IX_Orders_IsUrgentStatus ON Orders(IsUrgent, Status);

クエリプランはインデックスシークを使用しますが、それでもこの単純なクエリに答えるのに必要な数よりも多くの読み取りを実行します。

アップデート

私はこれを行うことができます

SELECT TOP 1 rows FROM sys.partitions p
INNER JOIN sys.indexes i
ON i.name = 'IX_Orders_ShippedNonUrgent'
AND i.object_id = p.object_id
AND i.index_id = p.index_id

9回の読み取りで結果が得られますが、単純なCOUNT(*)クエリを使用する方がはるかに簡単で脆弱性の少ない方法があるはずです。

4

1 に答える 1

1

私が望んでいることは不可能のようです。最善の答えは、Nikola Markovinovićによるコメントに残されています。これは、フィルター処理されたインデックスを忘れて、代わりにインデックス付きビューを使用することです。

CREATE VIEW [dbo].vw_Orders_TotalShippedNonUrgent WITH SCHEMABINDING 
AS 
SELECT COUNT_BIG(*) AS TotalOrders 
  FROM dbo.Orders WHERE IsUrgent = 0 AND Status = 1;

CREATE UNIQUE CLUSTERED INDEX IX_vw_Orders_TotalShippedNonUrgent ON vw_Orders_TotalShippedNonUrgent(TotalOrders);

これにより、必要な要約統計ごとにビューとそのインデックスを作成し、単純なアプローチではなくビューを要求するようにクエリを書き直す必要がありますが、読み取りは 2 回だけで高速です。

誰かが同じくらい高速なより単純なアプローチを持っている場合に備えて、この質問はしばらく開いたままにします。

于 2012-09-03T22:44:34.233 に答える