Database
 sql >> база данни >  >> RDS >> Database

Продължение №1 за водещи търсения с заместващи знаци

В последната си публикация „Един от начините да получите търсене на индекс за водещ заместващ знак“ споменах, че вие ще се нуждаят от тригери, за да се справят с поддържането на препоръчаните от мен фрагменти. Няколко души се свързаха с мен, за да попитат дали мога да демонстрирам тези задействания.

За да опростим от предишната публикация, нека приемем, че имаме следните таблици – набор от компании и след това таблица на 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

Резюме

Тази публикация имаше за цел да покаже колко лесно е да настроите тригери, които ще поддържат търсени фрагменти на низове за подобряване на търсенето с заместващи знаци, поне за низове със среден размер. Сега все още знам, че този вид се вижда като шантава идея, но продължавам да говоря за това, защото съм убеден, че има добри случаи на употреба.

В следващата си публикация ще покажа как да видя ефекта от този избор:Можете лесно да настроите представителни работни натоварвания, за да сравните разходите за ресурси за поддържане на фрагментите с икономиите на производителност по време на заявка. Ще разгледам различни дължини на низовете, както и различни баланси на работното натоварване (предимно четене спрямо предимно писане) и ще се опитам да намеря сладки места и опасни зони.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Сравняване на обекти по стойност. Част 6:Прилагане на структурното равенство

  2. SQL SELECT SUM

  3. SQL КЛЮЧОВЕ

  4. Как да добавите колона в SQL

  5. Подобряване на медианното решение за номериране на редове