Dalam posting terakhir saya, "Salah satu cara untuk mendapatkan pencarian indeks untuk wildcard terkemuka," saya menyebutkan bahwa Anda akan membutuhkan pemicu untuk menangani pemeliharaan fragmen yang saya rekomendasikan. Beberapa orang telah menghubungi saya untuk menanyakan apakah saya dapat menunjukkan pemicu tersebut.
Untuk menyederhanakan dari posting sebelumnya, mari kita asumsikan kita memiliki tabel berikut – satu set Perusahaan, dan kemudian tabel CompanyNameFragments yang memungkinkan pencarian pseudo-wildcard terhadap substring dari nama perusahaan:
CREATE TABLE dbo.Companies ( CompanyID int CONSTRAINT PK_Companies PRIMARY KEY, Name nvarchar(100) NOT NULL ); GO CREATE TABLE dbo.CompanyNameFragments ( CompanyID int NOT NULL, Fragment nvarchar(100) NOT NULL ); CREATE CLUSTERED INDEX CIX_CNF ON dbo.CompanyNameFragments(Fragment, CompanyID);
Mengingat fungsi ini untuk menghasilkan fragmen (satu-satunya perubahan dari artikel asli adalah saya telah meningkatkan @input
untuk mendukung 100 karakter):
CREATE FUNCTION dbo.CreateStringFragments( @input nvarchar(100) ) RETURNS TABLE WITH SCHEMABINDING AS RETURN ( WITH x(x) AS ( SELECT 1 UNION ALL SELECT x+1 FROM x WHERE x < (LEN(@input)) ) SELECT Fragment = SUBSTRING(@input, x, LEN(@input)) FROM x ); GO
Kita dapat membuat satu pemicu yang dapat menangani ketiga operasi:
CREATE TRIGGER dbo.Company_MaintainFragments ON dbo.Companies FOR INSERT, UPDATE, DELETE AS BEGIN SET NOCOUNT ON; DELETE f FROM dbo.CompanyNameFragments AS f INNER JOIN deleted AS d ON f.CompanyID = d.CompanyID; INSERT dbo.CompanyNameFragments(CompanyID, Fragment) SELECT i.CompanyID, fn.Fragment FROM inserted AS i CROSS APPLY dbo.CreateStringFragments(i.Name) AS fn; END GO
Ini berfungsi tanpa memeriksa jenis operasi apa yang terjadi karena:
- Untuk UPDATE atau DELETE, DELETE akan terjadi – untuk UPDATE, kami tidak akan repot-repot mencoba mencocokkan fragmen yang akan tetap sama; kami hanya akan meledakkan mereka semua, sehingga mereka dapat diganti secara massal. Untuk INSERT, pernyataan DELETE tidak akan berpengaruh, karena tidak akan ada baris di
deleted
. - Untuk INSERT atau UPDATE, INSERT akan terjadi. Untuk DELETE, pernyataan INSERT tidak akan berpengaruh, karena tidak akan ada baris di
inserted
.
Sekarang, untuk memastikannya berfungsi, mari lakukan beberapa perubahan pada Companies
tabel dan kemudian periksa dua tabel kami.
-- First, let's insert two companies -- (table contents after insert shown in figure 1 below) INSERT dbo.Companies(Name) VALUES(N'Banana'), (N'Acme Corp'); -- Now, let's update company 2 to 'Orange' -- (table contents after update shown in figure 2 below): UPDATE dbo.Companies SET Name = N'Orange' WHERE CompanyID = 2; -- Finally, delete company #1 -- (table contents after delete shown in figure 3 below): DELETE dbo.Companies WHERE CompanyID = 1;
Gambar 1: Isi tabel awal | Gambar 2: Isi tabel setelah pembaruan | Gambar 3: Isi tabel setelah dihapus |
Peringatan (untuk orang-orang integritas referensial)
Perhatikan bahwa jika Anda mengatur kunci asing yang tepat di antara dua tabel ini, Anda harus menggunakan pemicu alih-alih untuk menangani penghapusan, jika tidak, Anda akan mengalami masalah ayam dan telur – Anda tidak dapat menunggu sampai *setelah* induk baris dihapus untuk menghapus baris anak. Jadi, Anda harus menyiapkan ON DELETE CASCADE
(yang saya pribadi cenderung tidak suka), atau dua pemicu Anda akan terlihat seperti ini (pemicu setelahnya masih harus melakukan pasangan DELETE/INSERT dalam kasus UPDATE):
CREATE TRIGGER dbo.Company_DeleteFragments ON dbo.Companies INSTEAD OF DELETE AS BEGIN SET NOCOUNT ON; DELETE f FROM dbo.CompanyNameFragments AS f INNER JOIN deleted AS d ON f.CompanyID = d.CompanyID; DELETE c FROM dbo.Companies AS c INNER JOIN deleted AS d ON c.CompanyID = d.CompanyID; END GO CREATE TRIGGER dbo.Company_MaintainFragments ON dbo.Companies FOR INSERT, UPDATE AS BEGIN SET NOCOUNT ON; DELETE f FROM dbo.CompanyNameFragments AS f INNER JOIN deleted AS d ON f.CompanyID = d.CompanyID; INSERT dbo.CompanyNameFragments(CompanyID, Fragment) SELECT i.CompanyID, fn.Fragment FROM inserted AS i CROSS APPLY dbo.CreateStringFragments(i.Name) AS fn; END GO
Ringkasan
Pos ini ditujukan untuk menunjukkan betapa mudahnya menyiapkan pemicu yang akan mempertahankan dapat dicari fragmen string untuk meningkatkan pencarian wildcard, setidaknya untuk string berukuran sedang. Sekarang, saya masih tahu bahwa jenis ini dianggap sebagai ide yang aneh, tetapi saya terus membicarakannya karena saya yakin ada kasus penggunaan yang baik di luar sana.
Dalam posting saya berikutnya, saya akan menunjukkan bagaimana melihat dampak dari pilihan ini:Anda dapat dengan mudah mengatur beban kerja yang representatif untuk membandingkan biaya sumber daya untuk mempertahankan fragmen terhadap penghematan kinerja pada waktu kueri. Saya akan melihat panjang string yang bervariasi serta keseimbangan beban kerja yang berbeda (kebanyakan membaca vs. kebanyakan menulis) dan mencoba menemukan titik manis dan zona bahaya.