sql >> Database teknologi >  >> RDS >> Database

Opfølgning #1 på førende wildcard-søgninger

I mit sidste indlæg, "En måde at få et indekssøgning efter et førende jokertegn," nævnte jeg, at du ville have brug for triggere for at håndtere vedligeholdelsen af ​​de fragmenter, jeg anbefalede. Et par personer har kontaktet mig for at spørge, om jeg kunne demonstrere disse triggere.

For at forenkle fra det forrige indlæg, lad os antage, at vi har følgende tabeller – et sæt af virksomheder og derefter en CompanyNameFragments-tabel, der tillader pseudo-wildcard-søgning mod enhver understreng af firmanavnet:

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

Givet denne funktion til at generere fragmenter (den eneste ændring fra den originale artikel er, at jeg har øget @input for at understøtte 100 tegn):

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

Vi kan oprette en enkelt trigger, der kan håndtere alle tre operationer:

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

Dette fungerer uden at kontrollere, hvilken type operation der skete, fordi:

  • For en OPDATERING eller en SLETT sker SLET – for en OPDATERING vil vi ikke bryde os om at prøve at matche fragmenter, der forbliver de samme; vi skal bare blæse dem alle sammen, så de kan udskiftes i massevis. For en INSERT vil DELETE-sætningen ikke have nogen effekt, fordi der ikke vil være nogen rækker i deleted .
  • For en INSERT eller en OPDATERING vil INSERT ske. For en DELETE vil INSERT-sætningen ikke have nogen effekt, fordi der ikke vil være nogen rækker i inserted .

Nu, bare for at sikre, at det virker, lad os udføre nogle ændringer i Companies bord og derefter inspicere vores to borde.

-- 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;
Figur 1: Indledende tabelindhold Figur 2: Tabelindhold efter opdatering Figur 3: Tabelindhold efter sletning

Advarsel (til de referentielle integritetsfolk)

Bemærk, at hvis du opsætter korrekte fremmednøgler mellem disse to tabeller, bliver du nødt til at bruge en i stedet for trigger for at håndtere sletninger, ellers vil du have et kylling og æg problem – du kan ikke vente til *efter* forælderen række slettes for at fjerne underordnede rækker. Så du bliver nødt til at konfigurere ON DELETE CASCADE (hvilket jeg personligt ikke plejer at kunne lide), ellers ville dine to triggere se sådan ud (efter-triggeren skulle stadig udføre et DELETE/INSERT-par i tilfælde af en OPDATERING):

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

Oversigt

Dette indlæg havde til formål at vise, hvor nemt det er at konfigurere triggere, der forbliver søgelige strengfragmenter for at forbedre jokertegnsøgninger, i det mindste for strenge af moderat størrelse. Nu ved jeg stadig, at denne slags kommer til at virke som en skør idé, men jeg bliver ved med at tale om det, fordi jeg er overbevist om, at der er gode use cases derude.

I mit næste indlæg vil jeg vise, hvordan du kan se virkningen af ​​dette valg:Du kan nemt opsætte repræsentative arbejdsbelastninger for at sammenligne ressourceomkostningerne ved at vedligeholde fragmenterne med ydeevnebesparelserne på forespørgselstidspunktet. Jeg vil se på varierende strenglængder samt forskellige workload balancer (for det meste læs vs. mest skriv) og forsøge at finde sweet spots og farezoner.


  1. Sådan bruger du SQL-markører til specielle formål

  2. IP-adresse lagring i MySQL-database ved hjælp af PHP

  3. Overvågning af læse-/skriveforsinkelse

  4. Simpel parametrering og trivielle planer — del 1