web-dev-qa-db-ja.com

MSSQL-SUM(decimal)を使用したインデックス付きビューでの集約プッシュダウンをサポートする方法

非クラスター化列ストアインデックスを含むインデックス付きビューがあります。

_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
);
_
4
Lukas.Navratil

ビュー+ 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';
_

結果

enter image description here

ローカルに集計された行とバッチモードでのスキャンのプロパティ:

enter image description here

この例を機能させるには、ビュー+ WITH(NOEXPAND)のクエリが必要です。

WITH(NOEXPAND)を使用する理由は他にもたくさんあります。これの他の理由 blogpostPaul White による

3
Randi Vertongen