Membuat Indeks Non-Clustered pada Non-Persistent Kolom SQL Server

10

Saya berjuang untuk menemukan dokumentasi tentang bagaimana SQL Server benar-benar menyimpan kolom yang dikomputasi non-persisten.

Ambil contoh berikut:

--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

Saya mendapatkan bahwa itu disimpan pada tingkat daun, tetapi jika nilainya tidak bertahan bagaimana bisa disimpan? Bagaimana indeks membantu SQL Server menemukan baris ini dalam situasi ini?

Setiap bantuan sangat dihargai,

Terimakasih banyak,

EDIT:

Terima kasih kepada Brent & Aaron untuk menjawab ini, inilah PasteThePlan dengan jelas menunjukkan apa yang mereka jelaskan.

Uberzen1
sumber
5
Itu tidak bertahan di halaman data dari tabel, tetapi itu tetap di halaman indeks .
Aaron Bertrand
Kolom terhitung yang tidak bertahan tidak disimpan secara fisik dalam tabel. Mereka adalah kolom virtual. Nilai-nilai mereka dihitung ulang setiap kali direferensikan dalam kueri. lihat referensi ini .
Kin Shah

Jawaban:

11

Ketika SQL Server membuat indeks pada bidang yang dihitung, bidang yang dihitung ditulis ke disk pada saat itu - tetapi hanya pada 8K halaman indeks itu. SQL Server dapat menghitung InvoiceStatusID saat membaca melalui indeks berkerumun - tidak perlu menulis data itu ke indeks berkerumun.

Saat Anda menghapus / memperbarui / menyisipkan baris dalam dbo.Invoice, data dalam indeks tetap diperbarui. (Ketika InvoiceStatus berubah, SQL Server juga tahu untuk memperbarui IX_Invoice.)

Cara terbaik untuk melihat ini sendiri adalah dengan benar-benar melakukannya: buat objek ini, dan jalankan pembaruan yang menyentuh bidang InvoiceStatusID. Posting rencana eksekusi (PasteThePlan.com sangat membantu untuk ini) jika Anda ingin bantuan melihat di mana pembaruan indeks terjadi.

Brent Ozar
sumber
1
@ Uberzen1 Tidak, saat ia menjelaskan, itu ditulis ke halaman indeks pada waktu insert / update. Itu tidak harus menghitung ulang apa pun jika indeks digunakan untuk mengakses kolom.
Aaron Bertrand
Ah! Aku bersamamu sekarang, maaf!
Uberzen1
6
@blobbles baik, jangan tersinggung, tapi saya tidak berpikir itu pada Brent. Mereka dapat menempelkan XML yang sama ke dropbox, forum MSDN, di sini, pada dasarnya di mana saja daring ... apakah setiap layanan daring sekarang harus bertanggung jawab atas rahasia yang mungkin diungkapkan oleh orang yang mengunggah file di sana?
Aaron Bertrand
2
@blobbles ya, Anda tidak bisa menghentikan orang dari berbagi berlebihan. Hei, ngomong-ngomong, ikuti saya di Instagram - Saya BrentO - dan saya berbagi foto sarapan saya di sana. ;-)
Brent Ozar
4
@blobbles di tautan Privasi, ini menyatakan: Data yang Anda salin / tempelkan di sini bersifat publik . Siapa pun dapat membacanya. Tidak ada keamanan.
ypercubeᵀᴹ
8

Nilai untuk kolom indeks yang diindeks dan tidak bertahan tidak bertahan di halaman data tabel , tetapi tetap di halaman indeks . Itu tetap non-persisten dalam tabel, terlepas dari apakah itu tetap di 0, 1, atau beberapa indeks.

Hanya untuk mengilustrasikan deskripsi Brent, dengan mengambil contoh yang Anda berikan, mari kita masukkan baris:

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

Sekarang, mari kita lihat halaman indeks:

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

(Jelas berubah dbname, dan ID indeks mungkin tidak 2 dalam kasus Anda.)

Output (milik Anda pasti akan berbeda):

masukkan deskripsi gambar di sini

Dan akhirnya, mari kita periksa halaman untuk PageType2:

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

(Anda mungkin perlu mengubah 7 untuk mencocokkan id basis data Anda, dan jika Anda memiliki banyak file data, Anda mungkin perlu mengubah argumen kedua untuk mencocokkan PageFIDdari hasil pertama.)

Keluaran:

masukkan deskripsi gambar di sini

Itu ada di halaman indeks.

Aaron Bertrand
sumber
Sangat keren, terima kasih Aaron. Alasan saya mengajukan pertanyaan pada awalnya adalah bahwa saya mengalami masalah nyata dalam menyebarkan indeks yang serupa di dunia nyata, dan ingin memahami dengan tepat apa yang sedang terjadi di bawah tenda sehingga saya dapat mengetahui masalahnya. Ini sangat membantu, terima kasih!
Uberzen1
1
@ Uberzen1 Bisakah Anda mendefinisikan "masalah nyata"? Apakah Anda akan memposting pertanyaan tentang itu masalah?
Aaron Bertrand
Saya mungkin melakukannya, saya akan menggali lebih dalam lagi terlebih dahulu, tetapi hanya ingin mengetahui apa yang sebenarnya dilakukan oleh pernyataan indeks. TLDR adalah; Saya memiliki meja besar yang mirip dengan tabel faktur di atas, memiliki sekitar 400m catatan dan, sayangnya kolom OrderStatus menampar tepat di tengahnya, membuat pengindeksan dll sedikit menyakitkan. Kami telah menambahkan kolom yang dihitung untuk sekarang bahwa kami akhirnya akan bertahan dan memindahkan bidang varchar ke tabel itu sendiri. 1/2
Uberzen1
5
@ Uberzen1 Ya, karena kolom yang dihitung sebenarnya terwujud ke disk saat menulis ke indeks, semua aktivitas itu harus dicatat. Solusinya mungkin berhenti mengandalkan kolom yang dihitung - baik menempatkan ekspresi itu ke tampilan atau permintaan ad hoc, dan jika itu bukan pilihan Anda bisa membuat kolom nullable baru, perbarui dalam potongan (untuk menghindari pembunuhan log) , lalu jatuhkan kolom yang dihitung, ganti nama kolom baru, dan ubah DML Anda untuk menulisnya secara manual. Tapi sungguh karena ini informasi yang berlebihan yang dapat Anda peroleh dari data yang ada, saya akan memilih opsi pertama.
Aaron Bertrand
2
Terima kasih banyak Aaron. Saya senang Anda menyebutkan meletakkan pandangan di depannya karena itu adalah solusi saya juga, mungkin sudah waktunya untuk meninjau kembali ide itu!
Uberzen1
7

Atribut PERSISTEDuntuk kolom yang dihitung berkaitan dengan apakah nilai-nilai tersebut bertahan dalam tabel (indeks berkerumun atau tumpukan) dan bukan apakah nilai-nilai tersebut bertahan dalam indeks.

The CREATE INDEXmemiliki persyaratan untuk keterbatasan mengenai kolom dihitung dan indeks:

Kolom yang dihitung yang bersifat deterministik dan tepat atau tidak tepat dapat dimasukkan dalam kolom. Kolom yang dihitung berasal dari gambar, nteks, teks, varchar (max), nvarchar (max), varbinary (max), dan tipe data xml dapat dimasukkan ke dalam kolom non-kunci selama tipe data kolom yang dikomputasi diperbolehkan sebagai termasuk kolom. Untuk informasi lebih lanjut, lihat Indeks pada Kolom yang Dihitung.

Tidak ada batasan apakah kolom yang dihitung tetap ada atau tidak.

dan selanjutnya (bukan tentang yang disertakan tetapi tentang kolom yang dihitung di bagian utama indeks):

Indeks dapat dibuat pada kolom yang dihitung. Selain itu, kolom yang dihitung dapat memiliki properti PERSISTED. Ini berarti bahwa Database Engine menyimpan nilai yang dihitung dalam tabel, dan memutakhirkannya ketika kolom lain yang bergantung pada kolom yang dihitung diperbarui. Mesin Database menggunakan nilai-nilai tetap ini ketika membuat indeks pada kolom, dan ketika indeks direferensikan dalam kueri.

Untuk mengindeks kolom yang dihitung, kolom yang dihitung harus (menjadi) deterministik dan tepat. Namun, menggunakan PERSISTEDproperti memperluas jenis kolom yang dihitung dapat diindeks untuk memasukkan:

...

ypercubeᵀᴹ
sumber