Gunakan fungsi "LEN" di klausa "WHERE" di "CREATE UNIQUE INDEX"
12
Saya punya tabel ini:
CREATETABLE Table01 (column01 nvarchar(100));
Dan saya ingin membuat indeks unik pada kolom01 dengan kondisi ini LEN (kolom01)> = 5
Saya mencoba:
CREATEUNIQUEINDEX UIX_01 ON Table01(column01)WHERE LEN(column01)>=5;
Saya mendapatkan:
Klausa WHERE salah untuk indeks yang difilter 'UIX_01' pada tabel 'Table01'.
Dan:
ALTERTABLE Table01 ADD column01_length AS(LEN(column01));CREATEUNIQUEINDEX UIX_01 ON Table01(column01)WHERE column01_length >=5;
Menghasilkan:
Indeks yang difilter 'UIX_01' tidak dapat dibuat di tabel 'Table01' karena kolom 'column01_length' dalam ekspresi filter adalah kolom yang dihitung. Tulis ulang ekspresi filter sehingga tidak termasuk kolom ini.
Salah satu metode untuk mengatasi pembatasan indeks yang difilter adalah dengan tampilan yang diindeks:
CREATETABLE dbo.Table01 (
Column01 NVARCHAR(100));
GO
CREATEVIEW dbo.vw_Table01_Column01_LenOver5Unique
WITH SCHEMABINDING ASSELECT Column01
FROM dbo.Table01
WHERE LEN(Column01)>=5;
GO
CREATEUNIQUECLUSTEREDINDEX cdx
ON dbo.vw_Table01_Column01_LenOver5Unique(Column01);
GO
INSERTINTO dbo.Table01 VALUES('1');--successINSERTINTO dbo.Table01 VALUES('1');--successINSERTINTO dbo.Table01 VALUES('55555');--successINSERTINTO dbo.Table01 VALUES('55555');--duplicate key error
GO
EDIT:
Bagaimana saya harus mendefinisikan tampilan jika saya memiliki dua kolom dalam indeks? BUAT INDEKS UNIK UIX_01 PADA Tabel01 (kolom01, kolom02) DI MANA LEN (kolom01)> = 5
Pendekatan tampilan yang diindeks dapat diperluas untuk kunci komposit dengan menambahkan kolom kunci lainnya ke definisi tampilan dan indeks. Filter yang sama diterapkan dalam definisi tampilan tetapi keunikan baris kualifikasi ditegakkan oleh kunci komposit daripada nilai kolom tunggal:
CREATETABLE dbo.Table01 (
Column01 NVARCHAR(100),Column02 NVARCHAR(100));
GO
CREATEVIEW dbo.vw_Table01_Column01_LenOver5Unique
WITH SCHEMABINDING ASSELECT Column01, Column02
FROM dbo.Table01
WHERE LEN(Column01)>=5;
GO
CREATEUNIQUECLUSTEREDINDEX cdx
ON dbo.vw_Table01_Column01_LenOver5Unique(Column01, Column02)
GO
INSERTINTO dbo.Table01 VALUES('1','A');--successINSERTINTO dbo.Table01 VALUES('1','A');--successINSERTINTO dbo.Table01 VALUES('55555','A');--successINSERTINTO dbo.Table01 VALUES('55555','B');--successINSERTINTO dbo.Table01 VALUES('55555','B');--duplicate key error
GO
Dan saya berharap ini akan melakukan jauh lebih baik daripada keburukan saya.
James Anderson
@Dan Guzman haruskah saya menggunakan 'WITH SCHEMABINDING'?
geek
2
@Jalil Ya, SCHEMABINDINGdiperlukan untuk tampilan yang diindeks. Implikasinya tentu saja Anda harus membuang tampilan sebelum mengubah tabel. Perkakas seperti SSDT akan menangani ketergantungan itu secara otomatis.
Dan Guzman
Bagaimana saya harus mendefinisikan tampilan jika saya memiliki dua kolom dalam indeks? BUAT INDEKS UNIK UIX_01 PADA Tabel01 (kolom01, kolom02) DI MANA LEN (kolom01)> = 5;
geek
@Jilil, saya menambahkan contoh kunci komposit untuk jawaban saya.
Dan Guzman
5
Ini tampaknya menjadi salah satu dari banyak batasan indeks yang difilter. Mencoba memintasnya dengan LIKEmenggunakan WHERE column01 LIKE '_____'tidak berhasil, menghasilkan pesan kesalahan yang sama ( "Klausa WHERE WHERE ..." ).
Selain VIEWsolusinya, cara lain adalah dengan mengonversi kolom yang dihitung menjadi kolom biasa dan menambahkan CHECKkendala sehingga selalu memiliki data yang valid:
Secara alami, itu berarti Anda harus secara eksplisit mengisi column01_lengthdengan panjang yang benar setiap kali Anda mengisi column01(pada sisipan dan pembaruan). Itu mungkin rumit, karena Anda perlu memastikan bahwa panjangnya dihitung dengan cara yang sama seperti fungsi T-SQL LEN()melakukannya. Secara khusus, spasi tambahan perlu diabaikan, yang belum tentu bagaimana panjangnya dihitung secara default dalam berbagai bahasa pemrograman tempat aplikasi klien ditulis. Logikanya mungkin mudah untuk diperhitungkan dalam pemanggil, tetapi Anda harus menyadari perbedaan di tempat pertama.
Opsi akan menjadi INSERT/UPDATEpemicu 1 untuk menyediakan nilai yang benar untuk kolom, sehingga muncul sebagai dihitung untuk aplikasi klien.
1 Seperti yang dijelaskan dalam Pemicu Dibandingkan dengan Kendala , Anda perlu menggunakan BUKAN pemicu untuk ini. Pemicu SETELAH tidak akan pernah mengeksekusi, karena panjang yang tidak ada akan gagal kendala cek dan bahwa, pada gilirannya, akan mencegah pemicu dari berjalan. BUKAN pemicu, bagaimanapun, memiliki batasannya sendiri (lihat Pedoman Perencanaan Pemicu DML untuk ikhtisar singkat).
Saya tidak yakin bagaimana ini akan dilakukan dan mungkin ada cara yang lebih mudah untuk mencapai ini yang saya abaikan, tetapi ini harus melakukan apa yang Anda butuhkan jika Anda hanya tertarik untuk menegakkan keunikan.
CREATETABLE dbo.Table01
(
Column01 NVARCHAR(100));
GO
CREATEFUNCTION dbo.ChkUniqueColumn01OverLen5()
RETURNS BIT
ASBEGINDECLARE@Result BIT,@Count BIGINT,@DistinctCount BIGINT
SELECT@Count = COUNT(Column01),@DistinctCount = COUNT(DISTINCT Column01)FROM Table01
WHERE LEN(Column01)>=5SELECT@Result =CASEWHEN@Count =@DistinctCount THEN1ELSE0ENDRETURN@Result
END;
GO
ALTERTABLE dbo.Table01
ADDCONSTRAINT Chk_UniqueColumn01OverLen5
CHECK(dbo.ChkUniqueColumn01OverLen5()=1);
GO
INSERT dbo.Table01 (Column01)VALUES(N'123'),(N'1234');
GO
INSERT dbo.Table01 (Column01)VALUES(N'12345');
GO
INSERT dbo.Table01 (Column01)VALUES(N'12345');-- Will fail
GO
INSERT dbo.Table01 (Column01)VALUES(N'123');-- Will pass
GO
UPDATE dbo.Table01
SET Column01 ='12345'WHERE Column01 ='1234'-- Will fail
GO
SELECT*FROM dbo.Table01;
GO
DROPTABLE Table01;DROPFUNCTION dbo.ChkUniqueColumn01OverLen5;
Menggunakan fungsi bernilai skalar dalam batasan pemeriksaan atau definisi kolom yang dikomputasi akan memaksa semua kueri yang menyentuh tabel untuk dijalankan secara seri, bahkan jika mereka tidak mereferensikan kolom.
Erik Darling
2
@sp_BlitzErik Yap dan itu mungkin bukan yang terburuk tentang solusi ini :). Saya hanya ingin melihat apakah itu akan berhasil, maka peringatan kinerja.
SCHEMABINDING
diperlukan untuk tampilan yang diindeks. Implikasinya tentu saja Anda harus membuang tampilan sebelum mengubah tabel. Perkakas seperti SSDT akan menangani ketergantungan itu secara otomatis.Ini tampaknya menjadi salah satu dari banyak batasan indeks yang difilter. Mencoba memintasnya dengan
LIKE
menggunakanWHERE column01 LIKE '_____'
tidak berhasil, menghasilkan pesan kesalahan yang sama ( "Klausa WHERE WHERE ..." ).Selain
VIEW
solusinya, cara lain adalah dengan mengonversi kolom yang dihitung menjadi kolom biasa dan menambahkanCHECK
kendala sehingga selalu memiliki data yang valid:Diuji di rextester.com
Secara alami, itu berarti Anda harus secara eksplisit mengisi
column01_length
dengan panjang yang benar setiap kali Anda mengisicolumn01
(pada sisipan dan pembaruan). Itu mungkin rumit, karena Anda perlu memastikan bahwa panjangnya dihitung dengan cara yang sama seperti fungsi T-SQLLEN()
melakukannya. Secara khusus, spasi tambahan perlu diabaikan, yang belum tentu bagaimana panjangnya dihitung secara default dalam berbagai bahasa pemrograman tempat aplikasi klien ditulis. Logikanya mungkin mudah untuk diperhitungkan dalam pemanggil, tetapi Anda harus menyadari perbedaan di tempat pertama.Opsi akan menjadi
INSERT/UPDATE
pemicu 1 untuk menyediakan nilai yang benar untuk kolom, sehingga muncul sebagai dihitung untuk aplikasi klien.1 Seperti yang dijelaskan dalam Pemicu Dibandingkan dengan Kendala , Anda perlu menggunakan BUKAN pemicu untuk ini. Pemicu SETELAH tidak akan pernah mengeksekusi, karena panjang yang tidak ada akan gagal kendala cek dan bahwa, pada gilirannya, akan mencegah pemicu dari berjalan. BUKAN pemicu, bagaimanapun, memiliki batasannya sendiri (lihat Pedoman Perencanaan Pemicu DML untuk ikhtisar singkat).
sumber
Saya tidak yakin bagaimana ini akan dilakukan dan mungkin ada cara yang lebih mudah untuk mencapai ini yang saya abaikan, tetapi ini harus melakukan apa yang Anda butuhkan jika Anda hanya tertarik untuk menegakkan keunikan.
sumber