複数のテーブルでのフリーテキストCOUNTクエリが非常に遅い
-
10-07-2019 - |
質問
2つのテーブルがあります:
**Product**
ID
Name
SKU
**Brand**
ID
Name
製品テーブルには約120Kのレコードがあります ブランドテーブルには3万件のレコードがあります
特定のキーワードに一致する名前とブランドを持つすべての製品の数を見つける必要があります。
次のようにフリーテキスト「含む」を使用します:
SELECT count(*)
FROM Product
inner join Brand
on Product.BrandID = Brand.ID
WHERE (contains(Product.Name, 'pants')
or
contains(Brand.Name, 'pants'))
このクエリには約 17秒かかります。 このクエリを実行する前に、FreeTextインデックスを再構築しました。
Product.Nameのみを確認する場合。クエリは1秒未満です。 Brand.Nameのみをチェックする場合も同じです。 OR条件を使用すると問題が発生します。
LIKEを使用するようにクエリを切り替えた場合:
SELECT count(*)
FROM Product
inner join Brand
on Product.BrandID = Brand.ID
WHERE Product.Name LIKE '%pants%'
or
Brand.Name LIKE '%pants%'
1秒かかります。
MSDNで次のことを読みました: http://msdn.microsoft.com /en-us/library/ms187787.aspx
複数のテーブルで検索するには、 FROM句の結合テーブル である結果セットで検索する 2つ以上のテーブルの積。
だから、INNER JOINEDテーブルをFROMに追加しました:
SELECT count(*)
FROM (select Product.Name ProductName, Product.SKU ProductSKU, Brand.Name as BrandName FROM Product
inner join Brand
on product.BrandID = Brand.ID) as TempTable
WHERE
contains(TempTable.ProductName, 'pants')
or
contains(TempTable.BrandName, 'pants')
これはエラーになります: フルテキストインデックスが作成されていないため、列 'ProductName'でCONTAINSまたはFREETEXT述語を使用できません。
では、質問は-なぜOR条件がクエリの遅延などを引き起こしているのでしょうか?
解決
ちょっとした試行錯誤の後、私はうまくいくと思われる解決策を見つけました。インデックス付きビューの作成が含まれます。
CREATE VIEW [dbo].[vw_ProductBrand]
WITH SCHEMABINDING
AS
SELECT dbo.Product.ID, dbo.Product.Name, dbo.Product.SKU, dbo.Brand.Name AS BrandName
FROM dbo.Product INNER JOIN
dbo.Brand ON dbo.Product.BrandID = dbo.Brand.ID
GO
CREATE UNIQUE CLUSTERED INDEX IX_VW_PRODUCTBRAND_ID
ON vw_ProductBrand (ID);
GO
次のクエリを実行した場合:
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
GO
SELECT count(*)
FROM Product
inner join vw_ProductBrand
on Product.BrandID = vw_ProductBrand.ID
WHERE (contains(vw_ProductBrand.Name, 'pants')
or
contains( vw_ProductBrand.BrandName, 'pants'))
もう一度 1秒かかります。
他のヒント
次のようなことを試しましたか:
SELECT count(*)
FROM Product
INNER JOIN Brand ON Product.BrandID = Brand.ID
WHERE CONTAINS((Product.Name, Brand.Name), 'pants')
同様の問題に遭遇しましたが、次のようにユニオンで修正しました:
SELECT *
FROM Product
inner join Brand
on Product.BrandID = Brand.ID
WHERE contains(Product.Name, 'pants')
UNION
SELECT *
FROM Product
inner join Brand
on Product.BrandID = Brand.ID
WHERE contains(Brand.Name, 'pants'))