前回の投稿で、「インデックスを取得して主要なワイルドカードを探す1つの方法」と述べました。私が推奨したフラグメントの維持に対処するためのトリガーが必要になります。何人かの人から、これらのトリガーを示すことができるかどうか尋ねられました。
前の投稿から単純化するために、次のテーブルがあると仮定します。Companiesのセットと、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
3つの操作すべてを処理できる単一のトリガーを作成できます。
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
これは、次の理由で発生した操作のタイプをチェックせずに機能します。
- UPDATEまたはDELETEの場合、DELETEが発生します。UPDATEの場合、同じままであるフラグメントを一致させようとすることはありません。それらをすべて吹き飛ばすだけなので、まとめて交換できます。 INSERTの場合、
deleted
に行がないため、DELETEステートメントは効果がありません。 。 - INSERTまたはUPDATEの場合、INSERTが発生します。 DELETEの場合、
inserted
に行がないため、INSERTステートメントは効果がありません。 。
それでは、動作を確認するために、Companies
にいくつかの変更を加えましょう。 テーブルを作成してから、2つのテーブルを調べます。
-- 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: 削除後のテーブルの内容 |
警告(参照整合性の人々向け)
これら2つのテーブルの間に適切な外部キーを設定した場合、削除を処理するためにトリガーの代わりに使用する必要があることに注意してください。そうしないと、鶏と卵の問題が発生します。親の*後*まで待つことはできません。行が削除され、子行が削除されます。したがって、ON DELETE CASCADE
を設定する必要があります (私は個人的には気に入らない傾向があります)、または2つのトリガーは次のようになります(UPDATEの場合、afterトリガーは引き続き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
概要
この投稿は、シーク可能を維持するトリガーを設定するのがいかに簡単かを示すことを目的としています。 少なくとも中程度のサイズの文字列の場合、ワイルドカード検索を改善するための文字列フラグメント。今でも、この種のアイデアが奇抜なアイデアとして出くわすことは知っていますが、そこには良いユースケースがあると確信しているので、私はそれについて話し続けています。
次の投稿では、この選択の影響を確認する方法を示します。代表的なワークロードを簡単に設定して、フラグメントを維持するためのリソースコストと、クエリ時のパフォーマンスの節約を比較できます。さまざまな文字列の長さとさまざまなワークロードのバランス(主に読み取りと主に書き込み)を調べて、スイートスポットと危険ゾーンを見つけようとします。