Database
 sql >> Datenbank >  >> RDS >> Database

Follow-up Nr. 1 zu führenden Wildcard-Suchvorgängen

In meinem letzten Beitrag "Eine Möglichkeit, eine Indexsuche nach einem führenden Platzhalter zu erhalten", habe ich erwähnt, dass Sie würde Trigger benötigen, um mit der Aufrechterhaltung der von mir empfohlenen Fragmente fertig zu werden. Ein paar Leute haben mich kontaktiert, um zu fragen, ob ich diese Auslöser demonstrieren könnte.

Um den vorherigen Beitrag zu vereinfachen, nehmen wir an, wir haben die folgenden Tabellen – eine Gruppe von Unternehmen und dann eine CompanyNameFragments-Tabelle, die eine Pseudo-Platzhalter-Suche nach einer beliebigen Teilzeichenfolge des Firmennamens ermöglicht:

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

Angesichts dieser Funktion zum Generieren von Fragmenten (die einzige Änderung gegenüber dem ursprünglichen Artikel ist, dass ich @input erhöht habe um 100 Zeichen zu unterstützen):

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

Wir können einen einzelnen Trigger erstellen, der alle drei Operationen verarbeiten kann:

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

Dies funktioniert ohne Überprüfung, welche Art von Operation stattgefunden hat, weil:

  • Für ein UPDATE oder ein DELETE wird das DELETE passieren – für ein UPDATE werden wir uns nicht die Mühe machen, Fragmente abzugleichen, die gleich bleiben; Wir werden sie einfach alle wegblasen, damit sie massenhaft ersetzt werden können. Bei einem INSERT hat die DELETE-Anweisung keine Auswirkung, da es keine Zeilen in deleted gibt .
  • Für ein INSERT oder ein UPDATE wird das INSERT ausgeführt. Bei einem DELETE hat die INSERT-Anweisung keine Auswirkung, da keine Zeilen in inserted werden .

Um sicherzustellen, dass es funktioniert, nehmen wir jetzt einige Änderungen an den Companies vor Tisch und inspizieren Sie dann unsere beiden Tische.

-- 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;
Abbildung 1: Anfangstabelleninhalt Abbildung 2: Tabelleninhalt nach Aktualisierung Abbildung 3: Tabelleninhalt nach dem Löschen

Warnhinweis (für Leute mit referenzieller Integrität)

Beachten Sie, dass Sie, wenn Sie die richtigen Fremdschlüssel zwischen diesen beiden Tabellen einrichten, einen anstelle von Trigger verwenden müssen, um Löschungen zu behandeln, sonst haben Sie ein Henne-Ei-Problem – Sie können nicht bis *nach* dem Elternteil warten Zeile wird gelöscht, um die untergeordneten Zeilen zu entfernen. Sie müssten also ON DELETE CASCADE einrichten (was mir persönlich eher nicht gefällt), oder Ihre beiden Trigger würden so aussehen (der After-Trigger müsste im Falle eines UPDATE immer noch ein DELETE/INSERT-Paar ausführen):

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

Zusammenfassung

Dieser Beitrag sollte zeigen, wie einfach es ist, Trigger einzurichten, die suchbar bleiben Zeichenfolgenfragmente zur Verbesserung der Wildcard-Suche, zumindest für mittelgroße Zeichenfolgen. Nun, ich weiß immer noch, dass so etwas wie eine verrückte Idee rüberkommt, aber ich rede immer wieder darüber, weil ich überzeugt bin, dass es da draußen gute Anwendungsfälle gibt.

In meinem nächsten Beitrag werde ich zeigen, wie sich diese Wahl auswirkt:Sie können ganz einfach repräsentative Workloads einrichten, um die Ressourcenkosten für die Verwaltung der Fragmente mit den Leistungseinsparungen zum Zeitpunkt der Abfrage zu vergleichen. Ich werde mir unterschiedliche Zeichenfolgenlängen sowie unterschiedliche Workload-Balances (meistens Lesen vs. hauptsächlich Schreiben) ansehen und versuchen, Sweet Spots und Gefahrenzonen zu finden.