非クラスター化列ストアインデックスを含むインデックス付きビューがあります。
_CREATE VIEW [dbo].[SalesAggregated] with schemabinding AS
SELECT
BusinessDate,
ItemId
,COUNT_BIG(*) AS NumberOfRows
,SUM(Gross) AS Gross
FROM [dbo].[myTable]
GROUP BY BusinessDate, ItemId
_
基になるテーブルのGross
は、decimal(18,6)として定義されているため、集約プッシュダウンをサポートしています。ただし、ビューのSUMの結果はdecimal(38,6)であり、これは集計プッシュダウンには大きすぎます。
CAST(SUM(Gross) as decimal(18,6))
を使用してキャストすることはできません。インデックス付きビューではサポートされていないため、一意のクラスター化インデックスを作成しようとすると、このエラーが発生します。
ビューの選択リストに集計関数またはグループ化列の結果の式が含まれているため、ビュー 'DemoDB.dbo.SalesAggregated'でクラスター化インデックス 'UX_SalesAggregated'を作成できません。選択リストから集計関数またはグループ化列の結果の式を削除することを検討してください。
基になるテーブルでフィールドをmoneyデータ型に変換することは可能な解決策の1つに思えますが、一部のフィールドでは小数点以下4桁より高い精度が必要なため、すべてのフィールドでこれを行うことはできません。
また、基になるテーブルがクエリされたときに、可能な場合はインデックス付きビューが自動的に使用されるように、自動インデックス付きビューマッチングが機能する必要があります。テーブルとインデックス付きビューの間に小数をマネーにキャストするビューを作成する場合、おそらくそれは不可能です。
たとえば、私はそのようなクエリを達成したいです
_SELECT SUM(Gross)
FROM myTable
WHERE BusinessDate = '2019-08-14';
_
インデックス付きビューで列ストアインデックススキャンを使用し、集計はこのスキャン内で行われます。 SalesAggregatedのGrossがdecimal(38,6)であり、集約プッシュダウンをサポートしていないため、これは現在不可能です。その結果、クエリオプティマイザーは(誤って)集約プッシュダウンが可能な非集約テーブルから列ストアをスキャンします。
それを達成する方法はありますか?
[〜#〜]更新[〜#〜]
したがって、問題は、基になるテーブルのPK列がインデックス付きビューの定義のGROUP BYの一部でない場合に限られます。以下のRandiの例は機能しますが、次のコードは上記のエラーで失敗します。
_CREATE TABLE [dbo].[myTable](
[ItemId] [int] PRIMARY KEY NOT NULL,
[BusinessDate] [date] NULL,
[Gross] [decimal](18, 6) NULL
);
GO
CREATE VIEW [dbo].[SalesAggregated] with schemabinding AS
SELECT
BusinessDate
,COUNT_BIG(*) AS NumberOfRows
,CAST(SUM(Gross) as [decimal](18, 6)) AS Gross
FROM [dbo].[myTable]
GROUP BY BusinessDate
GO
CREATE UNIQUE CLUSTERED INDEX CX_BusinessDate ON dbo.[SalesAggregated](BusinessDate);
CREATE NONCLUSTERED COLUMNSTORE INDEX [IX_SalesAggregated] ON [dbo].[SalesAggregated]
(
[Gross],
BusinessDate
);
_
ビュー+ NOEXPAND
を使用するようにクエリ自体を変更できる場合、これは機能します。
テーブル定義とテストデータ
_CREATE TABLE [dbo].[myTable](
[ItemId] [int] PRIMARY KEY NOT NULL,
[BusinessDate] [date] NULL,
[Gross] [decimal](18, 6) NULL
);
INSERT INTO
[dbo].[myTable] WITH(TABLOCK)
(BusinessDate,Itemid,Gross)
SELECT
TOP(5000000) CAST(getdate() AS DATE),
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)),
11.2142
FROM master..spt_values
CROSS APPLY master..spt_values spt2;
_
ビュー定義+インデックス
_CREATE VIEW [dbo].[SalesAggregated] with schemabinding AS
SELECT
BusinessDate,
ItemId
,COUNT_BIG(*) AS NumberOfRows
,CAST(SUM(Gross) as [decimal](18, 6)) AS Gross
FROM [dbo].[myTable]
GROUP BY BusinessDate, ItemId
GO
CREATE UNIQUE CLUSTERED INDEX CX_ItemId
ON dbo.[SalesAggregated](ItemId);
CREATE NONCLUSTERED COLUMNSTORE INDEX [IX_SalesAggregated] ON [dbo].[SalesAggregated]
(
[Gross],
BusinessDate
);
_
クエリ
_SELECT SUM(Gross)
FROM [dbo].[SalesAggregated] WITH(NOEXPAND)
WHERE BusinessDate = '2019-08-14';
_
結果
ローカルに集計された行とバッチモードでのスキャンのプロパティ:
この例を機能させるには、ビュー+ WITH(NOEXPAND)
のクエリが必要です。
WITH(NOEXPAND)
を使用する理由は他にもたくさんあります。これの他の理由 blogpostPaul White による