Database
 sql >> Baza danych >  >> RDS >> Database

Kontynuacja nr 1 w przypadku wiodących poszukiwań dzikich kart

W moim ostatnim poście „Jeden sposób na uzyskanie indeksu wyszukiwania wiodącego symbolu wieloznacznego” wspomniałem, że potrzebowałby wyzwalaczy, aby poradzić sobie z utrzymaniem zalecanych przeze mnie fragmentów. Kilka osób skontaktowało się ze mną, aby zapytać, czy mogę zademonstrować te wyzwalacze.

Upraszczając z poprzedniego wpisu, załóżmy, że mamy następujące tabele – zbiór firm, a następnie tabelę CompanyNameFragments, która pozwala na wyszukiwanie pseudo-wildcards w dowolnym podłańcuchu nazwy firmy:

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

Biorąc pod uwagę tę funkcję generowania fragmentów (jedyną zmianą w stosunku do oryginalnego artykułu jest zwiększenie @input do obsługi 100 znaków):

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

Możemy stworzyć pojedynczy wyzwalacz, który obsłuży wszystkie trzy operacje:

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

Działa to bez sprawdzania, jaki rodzaj operacji miał miejsce, ponieważ:

  • W przypadku UPDATE lub DELETE nastąpi DELETE – w przypadku UPDATE nie będziemy zawracać sobie głowy próbami dopasowania fragmentów, które pozostaną takie same; po prostu rozwalimy je wszystkie, żeby można je było zastępować masowo. W przypadku INSERT instrukcja DELETE nie przyniesie efektu, ponieważ w deleted nie będzie wierszy .
  • W przypadku INSERT lub UPDATE nastąpi INSERT. W przypadku DELETE instrukcja INSERT nie przyniesie efektu, ponieważ w inserted nie będzie wierszy .

Teraz, aby upewnić się, że to działa, zróbmy kilka zmian w Companies tabeli, a następnie sprawdź nasze dwa stoły.

-- 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;
Rysunek 1: Początkowa zawartość tabeli Rysunek 2: Zawartość tabeli po aktualizacji Rysunek 3: Zawartość tabeli po usunięciu

Zastrzeżenie (dla osób zajmujących się uczciwością referencyjną)

Zauważ, że jeśli ustawisz odpowiednie klucze obce między tymi dwiema tabelami, będziesz musiał użyć zamiast wyzwalacza do obsługi usuwania, w przeciwnym razie będziesz mieć problem z kurczakiem i jajkiem – nie możesz czekać, aż *po* rodzicu wiersz jest usuwany, aby usunąć wiersze podrzędne. Musisz więc ustawić ON DELETE CASCADE (którego osobiście nie lubię) lub dwa wyzwalacze wyglądałyby tak (wyzwalacz po nadal musiałby wykonać parę DELETE/INSERT w przypadku UPDATE):

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

Podsumowanie

Ten post miał na celu pokazanie, jak łatwo jest skonfigurować wyzwalacze, które utrzymają wyszukiwalne fragmenty ciągów w celu usprawnienia wyszukiwania symboli wieloznacznych, przynajmniej w przypadku ciągów o średniej wielkości. Teraz nadal wiem, że tego rodzaju pomysł wydaje się zwariowany, ale wciąż o tym mówię, ponieważ jestem przekonany, że istnieją dobre przypadki użycia.

W następnym poście pokażę, jak zobaczyć wpływ tego wyboru:Możesz łatwo skonfigurować reprezentatywne obciążenia, aby porównać koszty zasobów związane z utrzymywaniem fragmentów z oszczędnością wydajności w czasie wykonywania zapytania. Przyjrzę się różnym długościom ciągów, a także różnym równoważeniom obciążenia (głównie odczyt vs. głównie zapis) i spróbuję znaleźć idealne miejsca i niebezpieczne strefy.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Model bazy danych dla systemu rezerwacji szkoły nauki jazdy. Część 1

  2. Szybkie znajdowanie odrębnych wartości

  3. Zrozumienie systemu wejściowego i wyjściowego Hadoop

  4. Relacyjne bazy danych

  5. Filtrowane indeksy i dołączone kolumny