計算列にフィルター選択されたインデックスを作成できません


17

私の前の質問で、テーブルに新しい計算列を追加するときにロックエスカレーションを無効にすることは良い考えですか?、計算列を作成しています:

ALTER TABLE dbo.tblBGiftVoucherItem
ADD isUsGift AS CAST
(
    ISNULL(
        CASE WHEN sintMarketID = 2 
            AND strType = 'CARD'
            AND strTier1 LIKE 'GG%' 
        THEN 1 
        ELSE 0 
        END
    , 0) 
    AS BIT
) PERSISTED;

計算列はPERSISTEDであり、compute_column_definition(Transact-SQL)によると

執着

データベースエンジンがテーブルに計算値を物理的に格納し、計算列が依存する他の列が更新されたときに値を更新することを指定します。計算列にPERSISTEDのマークを付けると、確定的ではあるが正確ではない計算列にインデックスを作成できます。詳細については、計算列のインデックスを参照してください。パーティションテーブルのパーティション列として使用される計算列には、明示的にPERSISTEDのマークを付ける必要があります。PERSISTEDが指定されている場合、compute_column_expressionは確定的でなければなりません。

しかし、列にインデックスを作成しようとすると、次のエラーが表示されます。

CREATE INDEX FIX_tblBGiftVoucherItem_incl
ON dbo.tblBGiftVoucherItem (strItemNo) 
INCLUDE (strTier3)
WHERE isUsGift = 1;

フィルター式の列「isUsGift」は計算列であるため、フィルター索引「FIX_tblBGiftVoucherItem_incl」をテーブル「dbo.tblBGiftVoucherItem」に作成できません。この列が含まれないようにフィルター式を書き直してください。

計算列にフィルター処理されたインデックスを作成するにはどうすればよいですか?

または

代替ソリューションはありますか?


3
WHERE (sintMarketID = 2 AND strType = 'CARD' AND strTier1 LIKE 'GG%')ただし、フィルター処理されたインデックスを作成できます。
ypercubeᵀᴹ

回答:


20

残念ながら、SQL Server 2014の時点ではFiltered Index、フィルターが計算列にある場所を作成する機能はありません(永続化されているかどうかに関係なく)。

2009年以降、Connectアイテムが公開されています。先に進んで投票してください。たぶん、Microsoftはこれをいつか修正するでしょう。

Aaron Bertrandには、Filtered Indexesに関する他の多くの問題を扱った記事があります。


21

永続化された列にフィルター選択されたインデックスを作成することはできませんが、使用できる可能性があるかなり単純な回避策があります。

テストとして、IDENTITY列と、ID列に基づいて永続化された計算列を持つ単純なテーブルを作成しました。

USE tempdb;

CREATE TABLE dbo.PersistedViewTest
(
    PersistedViewTest_ID INT NOT NULL
        CONSTRAINT PK_PersistedViewTest
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , SomeData VARCHAR(2000) NOT NULL
    , TestComputedColumn AS (PersistedViewTest_ID - 1) PERSISTED
);
GO

次に、計算列にフィルターがあるテーブルに基づいてスキーマバインドビューを作成しました。

CREATE VIEW dbo.PersistedViewTest_View
WITH SCHEMABINDING
AS
SELECT PersistedViewTest_ID
    , SomeData 
    , TestComputedColumn
FROM dbo.PersistedViewTest
WHERE TestComputedColumn < CONVERT(INT, 27);

次に、スキーマバインドビューにクラスター化インデックスを作成しました。これには、計算列の値など、ビューに格納されている値を永続化する効果があります。

CREATE UNIQUE CLUSTERED INDEX IX_PersistedViewTest
ON dbo.PersistedViewTest_View(PersistedViewTest_ID);
GO

テーブルにいくつかのテストデータを挿入します。

INSERT INTO dbo.PersistedViewTest (SomeData)
SELECT o.name + o1.name + o2.name
FROM sys.objects o
    CROSS JOIN sys.objects o1
    CROSS JOIN sys.objects o2;

ビューに統計項目とインデックスを作成します。

CREATE STATISTICS ST_PersistedViewTest_View
ON dbo.PersistedViewTest_View(TestComputedColumn)
WITH FULLSCAN;

CREATE INDEX IX_PersistedViewTest_View_TestComputedColumn
ON dbo.PersistedViewTest_View(TestComputedColumn);

実行SELECT永続化列を持つテーブルに対して文はありクエリオプティマイザは、それがそうすることに意味が判断した場合は自動的に、永続的なビューを使用します。

SELECT pv.PersistedViewTest_ID
    , pv.TestComputedColumn
FROM dbo.PersistedViewTest pv
WHERE pv.TestComputedColumn = CONVERT(INT, 26)

上記のクエリの実際の実行計画は、クエリオプティマイザーが永続ビューを使用して結果を返すことを選択したことを示しています。

ここに画像の説明を入力してください

WHERE上記の節で明示的な変換に気づいたかもしれません。この明示CONVERT(INT, 26)により、クエリオプティマイザーは統計オブジェクトを適切に使用して、クエリによって返される行の数を推定できます。でクエリを記述した場合WHERE pv.TestComputedColumn = 26、実際には26がaと見なされるため、クエリオプティマイザーは行数を適切に推定できない場合がありますTINY INT。これにより、SQL Serverが永続ビューを使用しない場合があります。暗黙的な変換は非常に苦痛を伴う場合があり、比較と結合に正しいデータ型を一貫して使用することは有益です。

もちろん、スキーマバインディングを使用した結果生じる標準的な「落とし穴」はすべて、上記のシナリオに適用されます。これにより、すべてのシナリオでこの回避策を使用できなくなる場合があります。たとえば、最初にビューからスキーマバインディングを削除しないと、ベーステーブルを変更することはできなくなります。そのためには、ビューからクラスター化インデックスを削除する必要があります。

SQL Server Enterprise Editionがない場合、クエリオプティマイザーは、WITH (NOEXPAND)ヒントを使用してビューを直接参照しないクエリの永続ビューを自動的に使用しません。Enterprise Edition以外のバージョンで永続ビューを使用する利点を実現するには、上記のクエリを次のように書き換える必要があります。

SELECT pv.PersistedViewTest_ID
    , pv.TestComputedColumn
FROM dbo.PersistedViewTest_View pv WITH (NOEXPAND)
WHERE pv.TestComputedColumn = CONVERT(INT, 26)

上記のEnterprise Editionの制限を指摘してくれたIan Ringroseと、ヒントをくれたPaul Whiteに感謝し(NOEXPAND)ます。

Paulによるこの回答には、永続ビューに関連するクエリオプティマイザーに関する興味深い詳細が含まれています。


回避策は、クラスター化インデックスと非クラスター化インデックスの両方がビューに作成されることを示しています。何らかの理由で、非クラスター化インデックスをクラスター化インデックス上で使用する必要がありますか?または、非クラスター化インデックスのパフォーマンスが向上していますか?クラスター化インデックスがクエリで使用された場合、統計は何を示しますか?
ボブブライアン

興味深い質問、@ BobBryan-ビューを永続化するにはクラスター化インデックスが必要ですが、実際には一意のインデックスである必要はありません。TestComputedColumn代わりに、他の列にビューのクラスター化インデックスを作成することもできました。ただし、クラスター化インデックスにはテーブル/ビューのすべてのデータが含まれているため、単調に増加する数をクラスター化キーとして使用する方が良いと判断しました。注:私は実際にその仮定をテストしませんでした。実際、再現のいくつかのバリエーションについては正しくない可能性があります。
マックスヴァーノン

非クラスター化インデックスはカバリングインデックスではないため、ビューまたは基になるテーブルから列をフィルター処理、結合、または返すクエリは、ベーステーブルに対してキー検索操作を実行する必要があります。景色。実際のシナリオでは、パフォーマンスの向上を念頭に置いて、私の答えの限られた範囲を説明できる可能性があります。
マックスヴァーノン

4

from Create Indexwhereその節では、これは不可能です。

どこ

インデックスに含める行を指定して、フィルター選択されたインデックスを作成します。フィルター選択されたインデックスは、テーブルの非クラスター化インデックスである必要があります。フィルター選択されたインデックス内のデータ行のフィルター選択された統計を作成します。

フィルター述語は単純な比較ロジックを使用し、計算列、UDT列、空間データ型列、またはhierarchyIDデータ型列を参照できません。NULLリテラルを使用した比較は、比較演算子では許可されていません。代わりにIS NULLおよびIS NOT NULL演算子を使用してください。

ソース:MSDN


3
  • フィルター選択されたインデックスを配置するには、計算されていない列が必要です。
  • その列に入る値を計算する必要があります。

列を計算する前に、行が変更または挿入されるたびにトリガーを使用して列の値を計算しました。

(トリガーは、クエリで使用された2番目のテーブルからアイテムのPKを挿入/削除するためにも使用できます。)


3

これは、Max Vernonの回避策を改善する試みです。彼のソリューションでは、ビューで2つのインデックスと統計オブジェクトを使用することを提案しています。

最初のインデックスはクラスター化されます。これは、テーブル上の非クラスター化インデックスとは異なり、最初にクラスター化インデックスがなくてもビューで非クラスター化インデックスを作成しようとするとエラーが生成されるため、実際に必要です。

2番目のインデックスは、クエリの背後にあるインデックスとして使用される非クラスター化インデックスです。彼の回答のコメントセクションで、非クラスター化インデックスの代わりにクラスター化インデックスを使用するとどうなるかを尋ねました。

次の分析では、この質問に答えようとします。

ビューに非クラスター化インデックスを作成していないことを除いて、彼とまったく同じコードを使用しています。

統計オブジェクトも作成していません。従い、SQL Server Management Studio(SSMS)を使用して以下のコードを入力する場合、エラーのように見える赤い波線が表示される場合があることに注意してください。これらは(おそらく)エラーではありませんが、インテリセンスに問題があります。

インテリセンスを無効にするか、エラーを無視してコマンドを実行することができます。エラーなしで完了します。

-- Create the test table that uses a computed column.
USE tempdb;
CREATE TABLE dbo.PersistedViewTest
(
    PersistedViewTest_ID INT NOT NULL
    CONSTRAINT PK_PersistedViewTest
    PRIMARY KEY CLUSTERED
    IDENTITY(1,1)
    , SomeData VARCHAR(2000) NOT NULL
    , TestComputedColumn AS (PersistedViewTest_ID - 1) PERSISTED
);
GO

-- Insert some test data into the table.
INSERT INTO dbo.PersistedViewTest (SomeData)
SELECT o.name + o1.name + o2.name
FROM sys.objects o
    CROSS JOIN sys.objects o1
    CROSS JOIN sys.objects o2;
GO

次のクエリがテーブルに対して実行された後、次の実行プラン(ビュー/インデックスビューなし)が作成されます。

SELECT pv.PersistedViewTest_ID, pv.TestComputedColumn
FROM dbo.PersistedViewTest pv
WHERE pv.TestComputedColumn = CONVERT(INT, 26)
GO

ここに画像の説明を入力してください

これにより、比較するベースラインが得られます。クエリが完了した後、統計オブジェクトが作成されたことに注意してください(_WA_Sys_00000003_1FCDBCEB)。PK_PersistedViewTest統計オブジェクトは、クラスター化テーブルインデックスの作成時に作成されました。

次に、フィルタービューとそのビューのクラスター化インデックスが作成されます。

-- Create filtered view on the computed column.
CREATE VIEW dbo.PersistedViewTest_View
WITH SCHEMABINDING
AS
SELECT PersistedViewTest_ID, SomeData, TestComputedColumn
FROM dbo.PersistedViewTest
WHERE TestComputedColumn < CONVERT(INT, 27);
GO

-- Create unique clustered index to persist the values, including the computed column.
CREATE UNIQUE CLUSTERED INDEX IX_PersistedViewTest
ON dbo.PersistedViewTest_View(PersistedViewTest_ID);
GO

ここで、クエリを再度実行してみましょう。ただし、今回はビューに対して:

SELECT pv.PersistedViewTest_ID, pv.TestComputedColumn
FROM dbo.PersistedViewTest_View pv
WHERE pv.TestComputedColumn = CONVERT(INT, 26)
GO

新しい実行計画は次のとおりです。

ここに画像の説明を入力してください

新しいプランが考えられる場合、そのビューにビューとクラスター化インデックスを追加すると、クエリの実行に必要な時間が倍になったことを示す統計が表示されます。また、クエリの実行後に新しいインデックスをサポートするための新しい統計オブジェクトが作成されていないことに注意してください。これは、テーブルのクエリとは異なります。

クエリプランでは、非クラスター化インデックスの作成がクエリのパフォーマンスの向上に非常に役立つことを引き続き示しています。それでは、目的のパフォーマンスの向上を得るには、ビューに非クラスター化インデックスを追加する必要があるということですか?最後に試してみてください。「WITH NOEXPAND」オプションを使用するようにクエリを変更します。

SELECT pv.PersistedViewTest_ID, pv.TestComputedColumn
FROM dbo.PersistedViewTest_View pv WITH (NOEXPAND)
WHERE pv.TestComputedColumn = CONVERT(INT, 26)
GO

これにより、次のクエリプランが作成されます。

ここに画像の説明を入力してください

この実行計画は、Max Vernonの回答で指定された非クラスター化インデックスを使用して作成されたものと非常によく似ています。ただし、これは、1つ少ない(非クラスター化)インデックスと1つ少ない統計オブジェクトで行われます。

インデックス付きビューを適切に使用するには、SQL Serverの高速バージョンと標準バージョンでNOEXPANDオプションを使用する必要があることがわかります。Paul Whiteには、NOEXPANDオプションを使用する利点について詳しく説明した優れた記事があります。また、ビューインデックスによって提供される一意性保証がオプティマイザーによって使用されるように、このオプションをエンタープライズエディションで使用することをお勧めします。

上記の分析は、SQL Sever 2014のエクスプレス版で行われました。SQLServer 2016の開発者版でも試してみました。NOEXPANDオプションは、パフォーマンスの向上を達成するために開発版では必要ないようですが、それでも推奨されます。

5か月未満前に、Microsoftは開発者向けエディションを無料にしました。このライセンスでは、使用が開発のみに制限されています。つまり、本番環境ではデータベースを使用できません。したがって、メモリに最適化されたテーブル、暗号化、Rなどをテストする場合は、ライセンスなしの言い訳はできなくなります。数日前に問題なくSQL Server 2014 Expressと共にコンピューターに正常にインストールしました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.