La consulta de FreeText COUNT en varias tablas es súper lenta
-
10-07-2019 - |
Pregunta
Tengo dos tablas:
**Product**
ID
Name
SKU
**Brand**
ID
Name
La tabla de productos tiene aproximadamente 120K registros La tabla de marcas tiene 30K registros
Necesito encontrar el recuento de todos los productos con nombre y marca que coinciden con una palabra clave específica.
Uso el texto libre 'contiene' como este:
SELECT count(*)
FROM Product
inner join Brand
on Product.BrandID = Brand.ID
WHERE (contains(Product.Name, 'pants')
or
contains(Brand.Name, 'pants'))
Esta consulta lleva unos 17 segundos. Reconstruí el índice de FreeText antes de ejecutar esta consulta.
Si solo busco Product.Name. Ellos consultan menos de 1 seg. Lo mismo, si solo verifico el Brand.Name. El problema ocurre si uso la condición OR.
Si cambio la consulta para usar LIKE:
SELECT count(*)
FROM Product
inner join Brand
on Product.BrandID = Brand.ID
WHERE Product.Name LIKE '%pants%'
or
Brand.Name LIKE '%pants%'
Tarda 1 segundo.
Leí en MSDN que: http://msdn.microsoft.com /en-us/library/ms187787.aspx
Para buscar en varias tablas, use un tabla unida en su cláusula FROM a buscar en un conjunto de resultados que es el producto de dos o más mesas.
Entonces agregué una tabla 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')
Esto produce un error: No se puede usar un predicado CONTAINS o FREETEXT en la columna 'ProductName' porque no está indexado a texto completo.
Entonces la pregunta es: ¿por qué la condición OR podría estar causando, como una consulta lenta?
Solución
Después de un poco de prueba y error, encontré una solución que parece funcionar. Implica crear una vista indizada:
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 ejecuto la siguiente consulta:
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'))
Ahora toma 1 segundo nuevamente.
Otros consejos
¿Has probado algo como:
SELECT count(*)
FROM Product
INNER JOIN Brand ON Product.BrandID = Brand.ID
WHERE CONTAINS((Product.Name, Brand.Name), 'pants')
Me encontré con un problema similar pero lo solucioné con union, algo así como:
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'))