Cara terbaik sekitar indeks 16 kolom maks

8

Menurut CREATE INDEXdokumentasi:

Hingga 16 kolom dapat digabungkan menjadi satu kunci indeks komposit.

Kami punya tabel dengan ~ 18 kolom yang perlu membentuk kombinasi unik. Tabel ini tidak peka terhadap kinerja - kami jarang memperbarui nilai / memasukkan catatan. Kami hanya perlu memastikan bahwa kami menghindari duplikasi catatan kami ... dan berpikir kami bisa memaksakan kendala keunikan sederhana.

Ada ide? Saya terbuka untuk menghindari indeks unik / kendala sepenuhnya jika ada cara yang lebih baik.

Nick B
sumber
4
Itu meja.
@ Jo: tidak biasa dalam beberapa keadaan ketika Anda telah menggabungkan subtipe serupa menjadi satu. Dalam kasus saya, diperlukan 15 kolom kunci alih-alih 50+ tabel yang berbeda. Keputusan implementasi ...
gbn
Sementara apa yang Anda minta itu mungkin, saya tidak yakin itu bijaksana. Anda tidak mengikuti jalan yang dipukuli. Dengan demikian, Anda siap untuk kejutan. Anda lebih cenderung mempelajari kesalahan Anda sendiri daripada kesalahan orang lain. Dalam jangka panjang mungkin akan lebih mudah untuk mencoba pendekatan yang lebih konvensional. Jika Anda memposting detail lebih lanjut, kami dapat membantu penerapannya.
AK
Saya tahu ini sudah lama, tetapi apa yang mencegah Anda dari hanya menggunakan kolom identitas GUID?
Robert Harvey

Jawaban:

14

Tambahkan kolom yang dihitung terus - menerus yang menggabungkan 18 kunci, lalu buat indeks unik pada kolom yang dihitung:

alter table t add all_keys as c1+c2+c3+...+c18 persisted;
create unique index i18 on t (all_keys);

Lihat Membuat Indeks pada Kolom yang Dihitung .

Pendekatan lain adalah membuat tampilan yang diindeks:

create view v 
with schemabinding
as select c1+c2+c3+...+c18 as all_keys
from dbo.t;

create unique clustered index c18 on v(all_keys);

Lihat Membuat Tampilan Terindeks .

Kedua pendekatan memungkinkan untuk agregat kunci parsial: agregat c1 + c2 + c3 sebagai k1, c4 + c5 + c6 sebagai k2 dll. Kemudian indeks / buat tampilan yang diindeks pada (k1, k2, ...). Thia dapat bermanfaat untuk pemindaian rentang (indeks dapat digunakan untuk pencarian di c1 + c2 + c3.

Tentu saja, semua +operasi dalam contoh saya adalah agregasi string, operator yang sebenarnya untuk digunakan tergantung pada jenis semua kolom tersebut (mis. Anda mungkin harus menggunakan gips eksplisit).

PS. Karena kendala unik ditegakkan oleh indeks unik, segala pembatasan pada indeks unik akan berlaku untuk kendala unik juga:

create table t (
    c1 char(3), c2 char(3), c3 char(3), c4 char(3),
    c5 char(3), c6 char(3), c7 char(3), c8 char(3),
    c9 char(3), c10 char(3), c11 char(3), c12 char(3),
    c13 char(3), c14 char(3), c15 char(3), c16 char(3),
    c17 char(3), c18 char(3), c19 char(3), c20 char(3),
    constraint unq unique
      (c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18));
go  


Msg 1904, Level 16, State 1, Line 3
The index '' on table 't' has 18 column names in index key list. 
The maximum limit for index or statistics key column list is 16.
Msg 1750, Level 16, State 0, Line 3
Could not create constraint. See previous errors.

Namun, membuat batasan pada kolom yang dikomputasi tetap berfungsi:

create table t (
    c1 char(3), c2 char(3), c3 char(3), c4 char(3),
    c5 char(3), c6 char(3), c7 char(3), c8 char(3),
    c9 char(3), c10 char(3), c11 char(3), c12 char(3),
    c13 char(3), c14 char(3), c15 char(3), c16 char(3),
    c17 char(3), c18 char(3), c19 char(3), c20 char(3),
    all_c as 
        c1+c2+c3+c4+c5+c6+c7+c8+c9+c10+c11+
        c12+c13+c14+c15+c16+c17+c18 
        persisted
        constraint unq unique (all_c));
go  

Jelas, kolom yang bertahan mengkonsumsi ruang pada disk sehingga pendekatannya mungkin buruk untuk tabel yang sangat besar. Pendekatan tampilan diindeks tidak memiliki masalah ini, itu hanya mengkonsumsi ruang untuk indeks , bukan ruang untuk kolom dan indeks dihitung .

Remus Rusanu
sumber
1
Perhatikan batas kunci indeks 900 byte, tentu saja ...
gbn
1
@ GBN Ya, dan itulah sebabnya saya akhirnya menggunakan fungsi HashBytes seperti yang disarankan oleh RBarryYoung. Namun, saya menerima jawaban ini karena memberikan lebih banyak penjelasan dan eksplorasi berbagai metode. (Yaitu saya belajar banyak di sini)
Nick B
13

Saya pikir Anda akan melakukan jauh lebih baik untuk menempatkan cek indeks unik Anda pada kolom yang dihitung yang dihasilkan menggunakan HASHBYTES('MD5', ...)pada kombinasi 18 kolom Anda.

RBarryYoung
sumber
2

Saya mengalami masalah ini dan DBA senior saya menyarankan menggunakan fungsi pengecekan keunikan. Sisipan saya relatif kecil dan jarang (~ 1000 baris, dimasukkan pada awal setiap bulan) dan satu-satunya perhatian saya adalah menegakkan keunikan.

CREATE FUNCTION dbo.fn_UQ_table1 ()  
RETURNS BIT

AS
BEGIN
      DECLARE @ResultBit BIT = 1

      IF EXISTS(
      SELECT COUNT(*)
      FROM [table1]
      GROUP BY [c1],[c2],[c3],[c4],[c5],[c6],
            [c7],[c8],[c9],[c10],[c11],[c12],
            [c13],[c14],[c15],[c16]
      HAVING COUNT(*) > 1)
      SELECT @ResultBit = 0

      RETURN      @ResultBit

END

SELECT dbo.fn_UQ_table1()

ALTER TABLE [table1]  
WITH NOCHECK ADD  
CONSTRAINT [CK_UQ] CHECK  (([dbo].[fn_UQ_table1]()=1))

@RBarryYoung, saya belum memiliki perwakilan untuk berkomentar, tetapi saya mengalami masalah dengan solusi HASHBYTES karena salah satu tipe data saya adalah waktu, dan saya melakukan kesalahan pemula (?) Karena tidak memberikan argumen gaya opsional kepada saya Fungsi CONVERT ketika mengkonversi ke varchar. Tanpa gaya, Anda mendapatkan kesalahan berikut ketika Anda mencoba menambahkan PERSISTED UNIQUE NONCLUSTEREDkendala:

"column 'key_hash' in table 'table1' cannot be persisted because 
the column is non-deterministic."
Apoksi
sumber
0

Anda bisa menggabungkan beberapa nilai untuk membuat nilai unik baru dan menyimpannya di samping data saat ini.

Buat fungsi yang ditentukan pengguna untuk membuat nilai-nilai baru dan pemicu untuk mengisi bidang ketika data ditambahkan, maka Anda tidak memiliki banyak overhead dalam mempertahankan bidang.

Menggabungkan dua atau tiga bidang Anda akan membuat Anda di bawah batas 16.

Tony
sumber
-1 Saya tidak setuju dengan ide denormalisasi tabel demi menurunkan jumlah kolom.
Matt M
@Matt M - Saya tertarik untuk mengetahui mengapa Anda memilih jawaban saya ketika itu tidak terlalu berbeda dari saran pertama dalam jawaban yang diterima untuk pertanyaan ini? Saya juga ingin tahu mengapa Anda tidak setuju, apa solusi Anda?
Tony
Sebenarnya, saran Anda sebenarnya berbeda dari solusi yang diterima. Anda menganjurkan menggabungkan kolom, sedangkan solusi yang diterima menganjurkan membuat kolom baru yang berisi nilai-nilai gabungan. Solusi Anda berpotensi menghadirkan masalah kinerja melalui kueri yang terlalu rumit untuk memisahkan data berguna dari kolom gabungan Anda. Secara pribadi, saya akan menganjurkan solusi yang disajikan oleh RBarryYoung yang menggunakan kolom HashBytes gabungan PERSISTED gabungan ditempatkan ke dalam indeks yang unik. Sebaliknya, saya meningkatkan solusinya.
Matt M
@ Mat M - Terima kasih atas penjelasan Anda, tetapi saya memang mengatakan "... buat nilai unik baru dan simpan itu di samping data saat ini." Saya bermaksud kolom kunci baru menjadi bidang baru yang melengkapi data yang ada dan tidak menggantinya. Saya setuju bahwa penggunaan bidang hitung yang bertahan lebih baik daripada saran saya tentang UDF tetapi, dalam semangat, solusi saya sama.
Tony
Tampaknya saya salah membaca solusi Anda, dan saya mohon maaf untuk itu. Yang telah dikatakan, menggabungkan beberapa kolom bukanlah solusi yang baik, menurut pendapat saya, seperti solusi HashBytes yang diberikan. Saya akan menarik -1 saya. Sekali lagi, saya minta maaf atas pemahaman bacaan saya.
Matt M
0

Anda bisa menggunakan pemicu untuk insert/ update. Lakukan pengelompokan pilih berdasarkan kolom Anda dengan klausa having count(*) > 1. Jika itu kembali tidak kosong, putar kembali.

Ben Thul
sumber
0

Inilah yang akan saya lakukan. Saya akan membuat pemicu SETELAH untuk INSERT, PEMBARUAN yang melakukan ROW_NUMBER ()fungsi, dan partisi oleh semua 18 kolom unik Anda. Jika jumlah baris maksimum lebih dari satu, maka lakukan a ROLLBACK.

Thomas Stringer
sumber