使用 CONTAINS 进行全文搜索非常慢
Full text search with CONTAINS is very slow
我们尝试在 Azure 数据库上使用全文搜索,但在使用 CONTAINS 搜索时遇到了性能问题。
我们的数据采用星型模式,事实 table 启用了聚集列存储索引,大约有 4000 万行。下面是我们如何在维度上使用 CONTAINS 并在不同查询上对 Fact table 进行聚合:
使用 EXISTS 查询 1:
SELECT f.[FK_DimCompanyCodeId], SUM(f.NetValueInUSD)
FROM [SPENDBY].[FactInvoiceDetail] f
WHERE EXISTS (
SELECT * FROM [SPENDBY].[DimCompanyCode] d
WHERE f.[FK_DimCompanyCodeId] = d.Id
AND CONTAINS(d.*, 'Comcast'))
GROUP BY f.[FK_DimCompanyCodeId]
ORDER BY SUM(f.NetValueInUSD) DESC
此查询似乎 运行 永远不会 return 结果。
外键FK_DimCompanyCodeId]
上有非聚集索引,搜索时只有一行returned Comcast
:
SELECT id FROM [SPENDBY].[DimCompanyCode] d
WHERE CONTAINS(d.*, 'Comcast');
-- will return id = 5
并且有大约 2700 万行事实 table 其中有 FK_DimCompanyCodeId = 5
.
使用 INNER JOIN 的查询 2:
SELECT f.[FK_DimCompanyCodeId], SUM(f.NetValueInUSD)
FROM [SPENDBY].[FactInvoiceDetail] f
INNER JOIN [SPENDBY].[DimCompanyCode] d ON (f.[FK_DimCompanyCodeId] = d.Id)
WHERE CONTAINS(d.*, 'Comcast')
GROUP BY f.[FK_DimCompanyCodeId]
ORDER BY SUM(f.NetValueInUSD) DESC
这个查询似乎 运行 永远不会 return 结果也是如此。
查询 3 使用#temp table:
SELECT id INTO #temp FROM [SPENDBY].[DimCompanyCode] d
WHERE CONTAINS(d.*, 'Comcast');
SELECT f.[FK_DimCompanyCodeId], SUM(f.NetValueInUSD)
FROM [SPENDBY].[FactInvoiceDetail] f
WHERE EXISTS (
SELECT * FROM #temp
WHERE f.[FK_DimCompanyCodeId] = #temp.Id)
GROUP BY f.[FK_DimCompanyCodeId]
ORDER BY SUM(f.NetValueInUSD) DESC
很快,return5秒后出结果。
为什么案例 1 和案例 2 的全文搜索速度如此之慢。
问题是竞争索引——一个用于 JOIN
,一个用于过滤器。也许子查询会说服 SQL 服务器首先使用文本索引:
SELECT f.[FK_DimCompanyCodeId], SUM(f.NetValueInUSD)
FROM [SPENDBY].[FactInvoiceDetail] f JOIN
(SELECT id
FROM [SPENDBY].[DimCompanyCode] cc
WHERE CONTAINS(cc.*, 'Comcast')
) cc
ON cc.id = f.FK_DimCompanyCodeId
GROUP BY f.[FK_DimCompanyCodeId]
ORDER BY SUM(f.NetValueInUSD) DESC
如果您在 FactInvoiceDetail(FK_DimCompanyCodeId)
上有一个索引,它可能也会有所帮助。
最终,我发现 CONTAINS
在特定列上效果很好(例如 Description
):
SELECT f.[FK_DimCompanyCodeId], SUM(f.NetValueInUSD)
FROM [SPENDBY].[FactInvoiceDetail] f
WHERE f.[FK_DimCompanyCodeId] IN (
SELECT d.Id FROM [SPENDBY].[DimCompanyCode] d
WHERE CONTAINS(d.[Description], 'Comcast')
)
GROUP BY f.[FK_DimCompanyCodeId]
ORDER BY SUM(f.NetValueInUSD) DESC
为了搜索整个table,CONTAINSTABLE
会有最好的性能,避免使用#temp table:
SELECT f.[FK_DimCompanyCodeId], SUM(f.NetValueInUSD)
FROM [SPENDBY].[FactInvoiceDetail] f
LEFT OUTER JOIN CONTAINSTABLE([SPENDBY].[DimCompanyCode], *, '"Comcast"') ct
ON f.[FK_DimCompanyCodeId] = ct.[Key]
WHERE ct.[Key] IS NOT NULL
GROUP BY f.[FK_DimCompanyCodeId]
ORDER BY SUM(f.NetValueInUSD) DESC
我们尝试在 Azure 数据库上使用全文搜索,但在使用 CONTAINS 搜索时遇到了性能问题。
我们的数据采用星型模式,事实 table 启用了聚集列存储索引,大约有 4000 万行。下面是我们如何在维度上使用 CONTAINS 并在不同查询上对 Fact table 进行聚合:
使用 EXISTS 查询 1:
SELECT f.[FK_DimCompanyCodeId], SUM(f.NetValueInUSD)
FROM [SPENDBY].[FactInvoiceDetail] f
WHERE EXISTS (
SELECT * FROM [SPENDBY].[DimCompanyCode] d
WHERE f.[FK_DimCompanyCodeId] = d.Id
AND CONTAINS(d.*, 'Comcast'))
GROUP BY f.[FK_DimCompanyCodeId]
ORDER BY SUM(f.NetValueInUSD) DESC
此查询似乎 运行 永远不会 return 结果。
外键FK_DimCompanyCodeId]
上有非聚集索引,搜索时只有一行returned Comcast
:
SELECT id FROM [SPENDBY].[DimCompanyCode] d
WHERE CONTAINS(d.*, 'Comcast');
-- will return id = 5
并且有大约 2700 万行事实 table 其中有 FK_DimCompanyCodeId = 5
.
使用 INNER JOIN 的查询 2:
SELECT f.[FK_DimCompanyCodeId], SUM(f.NetValueInUSD)
FROM [SPENDBY].[FactInvoiceDetail] f
INNER JOIN [SPENDBY].[DimCompanyCode] d ON (f.[FK_DimCompanyCodeId] = d.Id)
WHERE CONTAINS(d.*, 'Comcast')
GROUP BY f.[FK_DimCompanyCodeId]
ORDER BY SUM(f.NetValueInUSD) DESC
这个查询似乎 运行 永远不会 return 结果也是如此。
查询 3 使用#temp table:
SELECT id INTO #temp FROM [SPENDBY].[DimCompanyCode] d
WHERE CONTAINS(d.*, 'Comcast');
SELECT f.[FK_DimCompanyCodeId], SUM(f.NetValueInUSD)
FROM [SPENDBY].[FactInvoiceDetail] f
WHERE EXISTS (
SELECT * FROM #temp
WHERE f.[FK_DimCompanyCodeId] = #temp.Id)
GROUP BY f.[FK_DimCompanyCodeId]
ORDER BY SUM(f.NetValueInUSD) DESC
很快,return5秒后出结果。
为什么案例 1 和案例 2 的全文搜索速度如此之慢。
问题是竞争索引——一个用于 JOIN
,一个用于过滤器。也许子查询会说服 SQL 服务器首先使用文本索引:
SELECT f.[FK_DimCompanyCodeId], SUM(f.NetValueInUSD)
FROM [SPENDBY].[FactInvoiceDetail] f JOIN
(SELECT id
FROM [SPENDBY].[DimCompanyCode] cc
WHERE CONTAINS(cc.*, 'Comcast')
) cc
ON cc.id = f.FK_DimCompanyCodeId
GROUP BY f.[FK_DimCompanyCodeId]
ORDER BY SUM(f.NetValueInUSD) DESC
如果您在 FactInvoiceDetail(FK_DimCompanyCodeId)
上有一个索引,它可能也会有所帮助。
最终,我发现 CONTAINS
在特定列上效果很好(例如 Description
):
SELECT f.[FK_DimCompanyCodeId], SUM(f.NetValueInUSD)
FROM [SPENDBY].[FactInvoiceDetail] f
WHERE f.[FK_DimCompanyCodeId] IN (
SELECT d.Id FROM [SPENDBY].[DimCompanyCode] d
WHERE CONTAINS(d.[Description], 'Comcast')
)
GROUP BY f.[FK_DimCompanyCodeId]
ORDER BY SUM(f.NetValueInUSD) DESC
为了搜索整个table,CONTAINSTABLE
会有最好的性能,避免使用#temp table:
SELECT f.[FK_DimCompanyCodeId], SUM(f.NetValueInUSD)
FROM [SPENDBY].[FactInvoiceDetail] f
LEFT OUTER JOIN CONTAINSTABLE([SPENDBY].[DimCompanyCode], *, '"Comcast"') ct
ON f.[FK_DimCompanyCodeId] = ct.[Key]
WHERE ct.[Key] IS NOT NULL
GROUP BY f.[FK_DimCompanyCodeId]
ORDER BY SUM(f.NetValueInUSD) DESC