La query COUNT di FreeText su più tabelle è super lenta
-
10-07-2019 - |
Domanda
Ho due tabelle:
**Product**
ID
Name
SKU
**Brand**
ID
Name
La tabella dei prodotti ha circa 120 KB di record La tabella dei marchi ha 30.000 record
Devo trovare il conteggio di tutti i prodotti con nome e marchio corrispondenti a una parola chiave specifica.
Uso il freetext 'contiene' in questo modo:
SELECT count(*)
FROM Product
inner join Brand
on Product.BrandID = Brand.ID
WHERE (contains(Product.Name, 'pants')
or
contains(Brand.Name, 'pants'))
Questa query richiede circa 17 secondi. Ho ricostruito l'indice di FreeText prima di eseguire questa query.
Se controllo solo Product.Name. Interrogano meno di 1 secondo. Lo stesso, se controllo solo Brand.Name. Il problema si verifica se utilizzo la condizione OR.
Se cambio query per usare LIKE:
SELECT count(*)
FROM Product
inner join Brand
on Product.BrandID = Brand.ID
WHERE Product.Name LIKE '%pants%'
or
Brand.Name LIKE '%pants%'
Ci vogliono 1 secondo.
Ho letto su MSDN che: http://msdn.microsoft.com /en-us/library/ms187787.aspx
Per cercare su più tabelle, utilizzare a tabella unita nella clausola FROM a cerca in un set di risultati che è il prodotto di due o più tabelle.
Quindi ho aggiunto una tabella INNER JOINED a 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')
Questo provoca un errore: Impossibile utilizzare un predicato CONTAINS o FREETEXT sulla colonna "ProductName" perché non è indicizzato full-text.
Quindi la domanda è: perché la condizione OR potrebbe causare una query lenta?
Soluzione
Dopo un po 'di prova, ho trovato una soluzione che sembra funzionare. Implica la creazione di una vista indicizzata:
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
Se eseguo la seguente query:
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'))
Ora ci vogliono 1 secondo di nuovo.
Altri suggerimenti
Hai provato qualcosa del tipo:
SELECT count(*)
FROM Product
INNER JOIN Brand ON Product.BrandID = Brand.ID
WHERE CONTAINS((Product.Name, Brand.Name), 'pants')
Ho riscontrato un problema simile ma l'ho risolto con l'unione, qualcosa del tipo:
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'))