Haruskah saya menandai indeks komposit sebagai unik jika berisi kunci primer?

9

Diberikan beberapa tabel dengan kunci utama, misalnya:

CREATE TABLE Customers (
   CustomerID int NOT NULL PRIMARY KEY,
   FirstName nvarchar(50),
   LastName nvarchar(50),
   Address nvarchar(200),
   Email nvarchar(260)
   --...
)

kami memiliki kunci primer unik CustomerID.

Secara tradisional saya mungkin perlu beberapa indeks penutup tambahan; misalnya untuk dengan cepat menemukan pengguna dengan salah satu CustomerIDatau Email:

CREATE INDEX IX_Customers_CustomerIDEmail ON Customers
(
   CustomerID,
   Email
)

Dan ini adalah jenis indeks yang telah saya buat selama beberapa dekade.

Tidak harus unik, tetapi sebenarnya memang begitu

Indeks itu sendiri ada untuk menghindari pemindaian tabel; ini adalah indeks penutup untuk membantu kinerja (indeks tidak ada sebagai kendala untuk menegakkan keunikan).

Hari ini saya ingat sedikit informasi - SQL Server dapat menggunakan fakta bahwa:

  • sebuah kolom memiliki batasan kunci asing
  • sebuah kolom memiliki indeks unik
  • kendala dipercaya

untuk membantu mengoptimalkan eksekusi permintaannya. Bahkan, dari Panduan Perancangan Indeks SQL Server :

Jika data unik dan Anda ingin menerapkan keunikan, membuat indeks unik alih-alih indeks nonunique pada kombinasi kolom yang sama memberikan informasi tambahan untuk pengoptimal kueri yang dapat menghasilkan rencana pelaksanaan yang lebih efisien . Membuat indeks unik (lebih disukai dengan membuat batasan UNIK) direkomendasikan dalam kasus ini.

Mengingat bahwa indeks multi-kolom saya berisi kunci utama, indeks komposit ini akan de facto menjadi unik. Ini bukan kendala yang saya butuhkan terutama SQL Server untuk menegakkan selama setiap memasukkan atau memperbarui; tetapi faktanya indeks non-cluster ini unik.

Apakah ada keuntungan dalam menandai indeks unik de facto ini sebagai benar-benar unik?

BUAT INDEKS UNIK IX_Customers_CustomerIDEmail ON pelanggan
(
   ID Pelanggan,
   Surel
)

Sepertinya saya bahwa SQL Server bisa cukup pintar untuk menyadari bahwa indeks saya sudah adalah unik berdasarkan fakta bahwa itu berisi kunci primer.

  • Tapi mungkin tidak tahu ini, dan ada keuntungan bagi pengoptimal jika saya menyatakan indeks sebagai unik.
  • Kecuali mungkin yang sekarang dapat menyebabkan pelambatan saat memasukkan dan memperbarui, di mana ia harus melakukan pemeriksaan keunikan - di mana sebelumnya tidak pernah harus sebelumnya.
  • Kecuali ia tahu indeks dijamin sudah unik, karena mengandung kunci utama.

Saya tidak dapat menemukan panduan apa pun dari Microsoft tentang apa yang harus dilakukan ketika indeks komposit berisi kunci utama.

Manfaat indeks unik meliputi:

  • Integritas data kolom yang ditentukan dipastikan.
  • Informasi tambahan yang bermanfaat bagi pengoptimal kueri disediakan.

Haruskah saya menandai indeks komposit sebagai unik jika sudah berisi kunci utama? Atau dapatkah SQL Server mencari tahu ini sendiri?

Ian Boyd
sumber
4
Untuk dengan cepat menemukan pelanggan oleh CustomerID, PK mungkin cukup. Untuk menemukan pelanggan melalui email, saya lebih suka memiliki indeks kedua hanya pada email (itu sudah mencakup kunci pengelompokan). Ngomong-ngomong, saya tahu ini fiktif, tapi ya, saya akan menandainya unik jika Anda tahu itu harus unik, bahwa kendala mungkin tidak cukup melukai sisipan untuk mengimbangi kasing yang dapat membantu pengoptimal. Tapi, itu semua tergantung pada beban kerja Anda, yang harus kami berspekulasi pada ...
Aaron Bertrand
1
Kami membahasnya panjang lebar di sini. Dan sementara kita tidak bisa memikirkan cara apa pun untuk menghasilkan data untuk membuktikannya dengan satu atau lain cara, kami mengasumsikan orang-orang SQL Server mungkin sangat cerdas, dan pengoptimal mungkin sudah dapat membuat meta optimasi ini. Kami juga memperkirakan itu mungkin yang terbaik untuk membuat indeks mengomunikasikan maksud - yang hanya merupakan indeks, dan tidak memaksakan keunikan sebagai aturan bisnis ketika sebaliknya hanya akan dilakukan untuk mencoba menebak kemampuan SQL Server yang kedua.
Ian Boyd

Jawaban:

11

Haruskah saya menandai indeks komposit sebagai unik jika sudah berisi kunci utama?

Mungkin tidak. Pengoptimal umumnya dapat menggunakan informasi tentang keunikan kolom kunci yang terkandung, jadi tidak ada keuntungan nyata.

Ada juga konsekuensi penting dari penandaan indeks yang unik pada rencana pembaruan yang memodifikasi kunci indeks itu untuk dipertimbangkan:

Mendirikan

CREATE TABLE dbo.Customers 
(
   CustomerID int NOT NULL PRIMARY KEY,
   FirstName nvarchar(50),
   LastName nvarchar(50),
   [Address] nvarchar(200),
   Email nvarchar(260)
);

CREATE NONCLUSTERED INDEX 
    IX_Customers_CustomerIDEmail 
ON dbo.Customers
(
   CustomerID,
   Email
);

-- Pretend we have some rows
UPDATE STATISTICS dbo.Customers 
WITH ROWCOUNT = 100000, PAGECOUNT = 20000;

Paket pembaruan per indeks (indeks tidak unik)

UPDATE dbo.Customers 
SET Email = N'New', [Address] = 'New Address'
WHERE Email = N'Old' 
OPTION (QUERYTRACEON 8790); -- Per-index update plan

Rencana eksekusi:

Split & Filter

Pengoptimal sering membuat keputusan berbasis biaya antara memperbarui indeks yang tidak tercakup per-baris (rencana 'sempit') atau per-indeks (rencana 'lebar'). Strategi default (kecuali untuk tabel OLTP dalam memori) adalah rencana yang luas.

Paket sempit (di mana indeks nonclustered dipertahankan pada saat yang sama dengan heap / clustered index) adalah pengoptimalan kinerja untuk pembaruan kecil. Optimalisasi ini tidak diterapkan untuk semua kasus - menggunakan fitur tertentu (seperti tampilan yang diindeks) berarti bahwa indeks terkait akan dipertahankan dalam rencana yang luas.

Informasi lebih lanjut: Mengoptimalkan T-SQL Query yang Mengubah Data

Dalam hal ini, saya telah menggunakan flag tanpa dokumen 8790 untuk memaksakan rencana pembaruan yang luas: Oleh karena itu rencana menunjukkan indeks berkerumun dan nonclustered dipertahankan secara terpisah.

Split mengubah setiap pembaruan menjadi pasangan hapus & sisipkan yang terpisah; Filter memfilter setiap baris yang tidak menghasilkan perubahan pada indeks.

Informasi lebih lanjut: ( Pembaruan yang tidak diperbarui ) oleh Tim QO SQL Server.

Paket pembaruan per indeks (indeks unik)

-- Same index, but unique
CREATE UNIQUE INDEX IX_Customers_CustomerIDEmail ON Customers
(
   CustomerID,
   Email
)
WITH (DROP_EXISTING = ON);

UPDATE dbo.Customers 
SET Email = N'New', [Address] = 'New Address'
WHERE Email = N'Old' 
OPTION (QUERYTRACEON 8790); -- Per-index update plan

Rencana eksekusi:

Split-Sort-Collapse

Perhatikan operator Urutkan dan Perkecil tambahan saat indeks ditandai unik.

Pola Split-Sort-Collapse ini diperlukan saat memperbarui kunci indeks unik, untuk mencegah pelanggaran kunci unik menengah.

Informasi lebih lanjut: Mempertahankan Indeks Unik oleh Craig Freedman

Sortir secara khusus dapat menjadi masalah. Tidak hanya itu merupakan biaya tambahan yang tidak perlu, tetapi juga dapat tumpah ke disk jika perkiraan tidak akurat.

Tentang kunci yang tidak tercakup

Faktor lain yang perlu dipertimbangkan adalah bahwa struktur indeks nonclustered selalu unik, di setiap tingkat indeks, bahkan jika UNIQUEtidak ditentukan. Kunci pengelompokan - dan mungkin pemersatu jika indeks pengelompokan tidak ditandai unik - ditambahkan ke indeks tidak tercakup yang tidak unik di semua tingkatan.

Sebagai konsekuensinya, definisi indeks berikut:

CREATE INDEX IX_Customers_CustomerIDEmail ON Customers
(
   Email
)
WITH (DROP_EXISTING = ON);

... sebenarnya berisi kunci (Email, CustomerID) di semua tingkatan. Oleh karena itu 'dicari' di kedua kolom:

SELECT * 
FROM dbo.Customers AS C WITH (INDEX(IX_Customers_CustomerIDEmail))
WHERE C.Email = N'Email'
AND C.CustomerID = 1;

Mencari

Informasi lebih lanjut: Lebih Lanjut Tentang Tombol Indeks Nonclustered oleh Kalen Delaney

Paul White 9
sumber
8

SQL tahu itu sudah unik (jika termasuk PK, itu tidak bisa lagi unik), terlepas dari apakah Anda secara eksplisit mengatakannya.

Perbedaan besar antara indeks non-unik dan indeks unik adalah bahwa indeks non-unik memerlukan kunci indeks berkerumun (dengan nilai uniquifier jika CIX tidak dinyatakan sebagai unik) di tingkat indeks yang lebih tinggi, tidak hanya di daun tingkat.

Dalam kasus Anda, Anda sudah memiliki CIX di kunci, yang berarti itu akan berada di setiap tingkat indeks.

Tapi Anda bisa membuat tabel yang memiliki PK terpisah (unik) dan CIX (tidak masalah). Kemudian buat indeks non-unik yang menyertakan PK pada kuncinya. Masukkan beberapa baris ke dalam tabel, termasuk beberapa nilai varchar yang mudah ditemukan untuk kolom CIX Anda. Masukkan cukup baris untuk menyebabkan beberapa level indeks Anda. Kemudian Anda dapat menggunakan DBCC IND untuk menemukan halaman di NCIX Anda, dan DBCC PAGE untuk membuka beberapa untuk melihat data, untuk melihat apakah nilai kunci CIX berada di level yang lebih tinggi.

Rob Farley
sumber