La requête FreeText COUNT sur plusieurs tables est très lente
-
10-07-2019 - |
Question
J'ai deux tables:
**Product**
ID
Name
SKU
**Brand**
ID
Name
La table des produits contient environ 120 000 enregistrements. La table des marques contient 30 000 enregistrements
Je dois trouver le nombre de tous les produits dont le nom et la marque correspondent à un mot clé spécifique.
J'utilise le texte libre 'contient' comme ceci:
SELECT count(*)
FROM Product
inner join Brand
on Product.BrandID = Brand.ID
WHERE (contains(Product.Name, 'pants')
or
contains(Brand.Name, 'pants'))
Cette requête prend environ 17 secondes. J'ai reconstruit l'index FreeText avant d'exécuter cette requête.
Si je ne vérifie que Product.Name. Leur requête est inférieure à 1 seconde. Pareil, si je vérifie seulement le nom Brand.Name. Le problème se produit si j'utilise la condition OR.
Si je change de requête pour utiliser LIKE:
SELECT count(*)
FROM Product
inner join Brand
on Product.BrandID = Brand.ID
WHERE Product.Name LIKE '%pants%'
or
Brand.Name LIKE '%pants%'
Cela prend 1 seconde.
J'ai lu sur MSDN que: http://msdn.microsoft.com /en-us/library/ms187787.aspx
Pour rechercher sur plusieurs tables, utilisez un table jointe dans votre clause FROM à rechercher sur un résultat qui est le produit de deux ou plusieurs tables.
J'ai donc ajouté une table 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')
Cela entraîne une erreur: Impossible d'utiliser un prédicat CONTAINS ou FREETEXT sur la colonne 'ProductName' car il n'est pas indexé en texte intégral.
La question est donc de savoir pourquoi une condition OR pourrait être provoquée, telle qu'une requête lente?
La solution
Après une petite tentative d’essai, j’ai trouvé une solution qui semble fonctionner. Cela implique la création d’une vue indexée:
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
Si j'exécute la requête suivante:
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'))
Il faut maintenant 1 seconde .
Autres conseils
Avez-vous essayé quelque chose comme:
SELECT count(*)
FROM Product
INNER JOIN Brand ON Product.BrandID = Brand.ID
WHERE CONTAINS((Product.Name, Brand.Name), 'pants')
J'ai rencontré un problème similaire, mais je l'ai résolu avec l'union, quelque chose comme:
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'))