非永続計算列SQLサーバーでの非クラスター化インデックスの作成


10

SQL Serverが非永続計算列を実際に格納する方法に関するドキュメントを見つけるのに苦労しています。

次の例を見てください。

--SCHEMA
CREATE TABLE dbo.Invoice
(
    InvoiceID INT IDENTITY(1, 1) PRIMARY KEY,
    CustomerID INT FOREIGN KEY REFERENCES dbo.Customer(CustomerID),
    InvoiceStatus NVARCHAR(50) NOT NULL,
    InvoiceStatusID AS CASE InvoiceStatus 
                         WHEN 'Sent' THEN 1 
                         WHEN 'Complete' THEN 2
                         WHEN 'Received' THEN 3
                       END
)
GO

--INDEX
CREATE NONCLUSTERED INDEX IX_Invoice ON Invoice
(
    CustomerID ASC
)
INCLUDE
(
    InvoiceStatusID
)
GO

リーフレベルで保存されていることがわかりますが、値が永続化されていない場合、どのように保存されますか?インデックスは、この状況でSQL Serverがこれらの行を見つけるのにどのように役立ちますか?

どんな助けも大歓迎です、

どうもありがとう、

編集:

これに答えてくれたBrent&Aaronに感謝します。ここにPasteThePlanがあり、彼らが説明したことを明確に示しています。


5
テーブルのデータページには保持されませんが、インデックスのページには保持されます
アーロンバートランド

永続化されていない計算列は、物理的にテーブルに格納されません。それらは仮想列です。それらの値は、クエリで参照されるたびに再計算されます。この参照を参照してください。
Kin Shah

回答:


11

SQL Serverが計算フィールドにインデックスを作成すると、計算フィールドはその時点でディスクに書き込まれますが、そのインデックスの8Kページにのみ書き込まれます。SQL Serverは、クラスター化インデックスを読み取るときにInvoiceStatusIDを計算できます。そのデータをクラスター化インデックスに書き込む必要はありません。

dbo.Invoiceの行を削除、更新、挿入すると、インデックスのデータは最新の状態に保たれます。(InvoiceStatusが変更されると、SQL ServerはIX_Invoiceも更新することを認識します。)

これを実際に確認する最良の方法は、実際にそれを行うことです。これらのオブジェクトを作成し、InvoiceStatusIDフィールドを変更する更新を実行します。インデックスの更新が行われている場所を確認したい場合は、実行計画を投稿してください(PasteThePlan.comがこれに役立ちます)。


1
@ Uberzen1いいえ、彼が説明したように、それは挿入/更新時にインデックスページに書き込まれます。インデックスを使用して列にアクセスする場合は、何も再計算する必要はありません。
アーロンバートランド

ああ!ごめんなさい、ごめんなさい!
Uberzen1 2017

6
@Bubblesは問題ありませんが、Brentについてはそうは思いません。彼らは同じXMLをDropbox、MSDNフォーラム、ここ、基本的にはオンラインのどこにでも貼り付けることができます...すべてのオンラインサービスは、ファイルをアップロードする人々が漏らす可能性のある秘密に責任を負う必要がありますか?
アーロンベルトラン

2
@blobblesええ、人々がオーバーシェアするのを止めることはできません。ところで、Instagramでフォローしてね-ブレントです-朝食の写真をそこで共有しています。;-)
ブレントオザー

4
プライバシーリンクの@blobblesには、次のように記載されています
ypercubeᵀᴹ

8

インデックス付きの非永続計算列の値は、テーブルのデータページに永続化されませんが、インデックスのページに永続化されます。0、1、または複数のインデックスに永続化されているかどうかに関係なく、テーブルに永続化されません。

ブレントの説明を説明するために、例を挙げて、行を挿入しましょう。

INSERT dbo.Invoice(CustomerID, InvoiceStatus) VALUES(1,N'Sent');

今、インデックスページを見てみましょう:

DBCC TRACEON(3604, -1);
DBCC IND(N'dbname', N'dbo.Invoice', 2);

(明らかに、を変更dbnameします。インデックスIDが2でない場合もあります。)

出力(あなたは確かに異なります):

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

そして最後に、ページのPageType2を検査します。

DBCC PAGE(7, 1, 584, 3);

(データベースIDと一致するように7を変更する必要がある可能性があります。複数のデータファイルがある場合はPageFID、最初の結果と一致するように2番目の引数を変更する必要があります。)

出力:

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

それはインデックスページにあります。


とてもクールです、アーロンに感謝します。最初に質問した理由は、現実の世界で同様のインデックスを展開するのに本当に問題があり、問題を理解するために内部で何が起こっているのかを正確に理解したかったからです。これは非常に役立ちます。
Uberzen1 2017

1
@ Uberzen1「本当の悩み」を定義できますか?その問題について質問を投稿ますか?
アーロンバートランド

最初にもう少し掘り下げるつもりでしたが、create indexステートメントが正確に何をしているのかを頭に入れたかっただけです。TLDRは、上記の請求書テーブルに似た大きなテーブルがあります。これには約400mのレコードがあり、残念ながらOrderStatus列がその真ん中を横切ってタップされているため、インデックス作成などが少し面倒です。とりあえず、計算列を追加しました。最終的に永続化し、varcharフィールドを独自のテーブルに移動します。1/2
Uberzen1 2017

5
@ Uberzen1ええ、計算列は実際にインデックスへの書き込み時にディスク上に実体化されるため、そのすべてのアクティビティをログに記録する必要があります。回避策は、計算された列への依存を停止することかもしれません-その式をビューまたはアドホッククエリに入れ、それがオプションでない場合は、新しいnull可能列を作成できます(ログの強制終了を回避するために)。 、次に計算された列をドロップし、新しい列の名前を変更して、DMLを変更して手動で書き込みます。しかし、実際には既存のデータから導出できる冗長な情報なので、最初のオプションを選択します。
アーロンベルトラン

2
アーロン、どうもありがとう。それが私の解決策への出発点でもあったので、その前にビューを置くことについてあなたが言ったのはうれしいです、おそらくそれはその考えを再訪する時です!
Uberzen1 2017

7

PERSISTED計算列の属性は、値がテーブル(クラスター化インデックスまたはヒープ)に保持されるかどうかに関係し、値がインデックスに保持されるかどうかには関係しません。

CREATE INDEXは、計算された列とインデックスに関する制限の要件があります。

確定的で、正確または不正確な計算列を列に含めることができます。画像、ntext、text、varchar(max)、nvarchar(max)、varbinary(max)、およびxmlデータ型から派生した計算列は、計算列のデータ型がインクルードとして許可されている限り、非キー列に含めることができます。カラム。詳細については、計算列のインデックスを参照してください。

計算列が永続化されるかどうかに制限はありません。

さらに(含まれるのではなく、インデックスの主要部分の計算列について):

インデックスは計算列に作成できます。さらに、計算列にはプロパティを含めることができますPERSISTED。つまり、データベースエンジンは計算された値をテーブルに格納し、計算された列が依存する他の列が更新されたときにそれらを更新します。データベースエンジンは、列にインデックスを作成するとき、およびインデックスがクエリで参照されるときに、これらの永続化された値を使用します。

計算列にインデックスを付けるには、計算列が決定的で正確でなければなりません。ただし、このPERSISTEDプロパティを使用すると、インデックス付け可能な計算列のタイプが拡張され、以下が含まれます。

...

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