В последната си публикация „Един от начините да получите търсене на индекс за водещ заместващ знак“ споменах, че вие ще се нуждаят от тригери, за да се справят с поддържането на препоръчаните от мен фрагменти. Няколко души се свързаха с мен, за да попитат дали мога да демонстрирам тези задействания.
За да опростим от предишната публикация, нека приемем, че имаме следните таблици – набор от компании и след това таблица на CompanyNameFragments, която позволява търсене с псевдоуместни знаци спрямо всеки подниз от името на компанията:
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);
Като се има предвид тази функция за генериране на фрагменти (единствената промяна спрямо оригиналната статия е, че увеличих @input
за поддържане на 100 знака):
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
Можем да създадем един тригер, който може да обработва и трите операции:
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
Това работи без проверка кой тип операция се е случила, защото:
- За АКТУАЛИЗИРАНЕ или ИЗТРИВАНЕ ще се случи ИЗТРИВАНЕТО – за АКТУАЛИЗИРАНЕ няма да се притесняваме да се опитваме да съпоставим фрагменти, които ще останат същите; просто ще ги издухаме всички, за да могат да се сменят масово. За INSERT изразът DELETE няма да има ефект, защото няма да има редове в
deleted
. - За INSERT или UPDATE, INSERT ще се случи. За DELETE операторът INSERT няма да има ефект, защото няма да има редове в
inserted
.
Сега, само за да сме сигурни, че работи, нека направим някои промени в Companies
таблица и след това проверете двете ни маси.
-- 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;
Фигура 1: Първоначално съдържание на таблица | Фигура 2: Съдържание на таблицата след актуализация | Фигура 3: Съдържание на таблицата след изтриване |
Предупреждение (за хора с референтна цялост)
Имайте предвид, че ако настроите правилни външни ключове между тези две таблици, ще трябва да използвате вместо тригер, за да обработвате изтривания, в противен случай ще имате проблем с пиле и яйце – не можете да чакате, докато *след* родителят ред се изтрива, за да се премахнат дъщерните редове. Така че ще трябва да настроите ON DELETE CASCADE
(което на мен лично не ми харесва) или вашите два тригера биха изглеждали така (следният тригер все пак ще трябва да извърши двойка DELETE/INSERT в случай на АКТУАЛИЗИРАНЕ):
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
Резюме
Тази публикация имаше за цел да покаже колко лесно е да настроите тригери, които ще поддържат търсени фрагменти на низове за подобряване на търсенето с заместващи знаци, поне за низове със среден размер. Сега все още знам, че този вид се вижда като шантава идея, но продължавам да говоря за това, защото съм убеден, че има добри случаи на употреба.
В следващата си публикация ще покажа как да видя ефекта от този избор:Можете лесно да настроите представителни работни натоварвания, за да сравните разходите за ресурси за поддържане на фрагментите с икономиите на производителност по време на заявка. Ще разгледам различни дължини на низовете, както и различни баланси на работното натоварване (предимно четене спрямо предимно писане) и ще се опитам да намеря сладки места и опасни зони.