sql >> Databáze >  >> RDS >> Database

Následný krok č. 1 při hledání vedoucích zástupných znaků

Ve svém posledním příspěvku „Jeden způsob, jak získat hledání indexu pro hlavní zástupný znak“, jsem zmínil, že bude potřebovat spouštěče, aby se vypořádal s udržováním fragmentů, které jsem doporučil. Pár lidí mě kontaktovalo se žádostí, zda bych mohl tyto spouštěče předvést.

Pro zjednodušení z předchozího příspěvku předpokládejme, že máme následující tabulky – sadu společností a pak tabulku CompanyNameFragments, která umožňuje vyhledávání pseudo-zástupných znaků proti libovolnému podřetězci názvu společnosti:

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);

Vzhledem k této funkci pro generování fragmentů (jediná změna oproti původnímu článku je, že jsem zvýšil @input pro podporu 100 znaků):

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

Můžeme vytvořit jediný spouštěč, který zvládne všechny tři operace:

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

Funguje to bez jakékoli kontroly, který typ operace se stal, protože:

  • U AKTUALIZACE nebo DELETE dojde k DELETE – v případě AKTUALIZACE se nebudeme obtěžovat snahou porovnat fragmenty, které zůstanou stejné; jen je všechny vyhodíme, aby se daly hromadně vyměnit. U příkazu INSERT nebude mít příkaz DELETE žádný účinek, protože v deleted nebudou žádné řádky .
  • V případě INSERT nebo AKTUALIZACE proběhne INSERT. U příkazu DELETE nebude mít příkaz INSERT žádný účinek, protože v inserted nebudou žádné řádky. .

Nyní, abychom se ujistili, že to funguje, proveďte nějaké změny v Companies stůl a poté si prohlédněte naše dva stoly.

-- 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;
Obrázek 1: Počáteční obsah tabulky Obrázek 2: Obsah tabulky po aktualizaci Obrázek 3: Obsah tabulky po smazání

Upozornění (pro lidi z referenční integrity)

Všimněte si, že pokud mezi těmito dvěma tabulkami nastavíte správné cizí klíče, budete muset ke zpracování mazání použít místo triggeru, jinak budete mít problém slepice a vejce – nemůžete čekat, až *po* rodič řádek je odstraněn, aby se odstranily podřízené řádky. Takže byste museli nastavit ON DELETE CASCADE (což se mi osobně nelíbí), nebo by vaše dva spouštěče vypadaly takto (spouštěč after by v případě AKTUALIZACE musel stále provádět pár 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

Shrnutí

Cílem tohoto příspěvku bylo ukázat, jak snadné je nastavit spouštěče, které udrží vyhledatelné fragmenty řetězců pro zlepšení vyhledávání zástupných znaků, alespoň pro středně velké řetězce. Nyní stále vím, že tento druh přichází jako šílený nápad, ale stále o tom mluvím, protože jsem přesvědčen, že existují dobré případy použití.

V mém dalším příspěvku ukážu, jak vidět dopad této volby:Můžete snadno nastavit reprezentativní pracovní zátěže, abyste porovnali náklady na prostředky na údržbu fragmentů s úsporami výkonu v době dotazu. Podívám se na různé délky řetězců a také na různé vyvážení pracovní zátěže (většinou čtení vs. většinou zápis) a pokusím se najít sladká místa a nebezpečné zóny.


  1. SCD typ 2

  2. Základní správa MaxScale pomocí MaxCtrl pro MariaDB Cluster

  3. PHP zobrazit obrázek BLOB z MySQL

  4. Mohu vytvořit pohled s parametrem v MySQL?