【发布时间】:2016-02-08 14:26:14
【问题描述】:
我在数据库 Products 和 Filters 中有两个表。
架构:
我创建了一个查询,从过滤器表中查找所有记录,循环每个记录并调用一个为 Products 表设置类别 ID 的过程。
过滤表数据如下。
过滤器选择查询如下..
DECLARE @TotalRecords INT, @Start INT, @Limit INT, @CatId INT, @Merchants NVARCHAR(max), @NotMatch NVARCHAR(max), @WillMatch NVARCHAR(max);
SELECT @TotalRecords = COUNT(*) FROM filters;
SET @Limit = 1;
SET @Start = 0;
WHILE(@TotalRecords > 0)
BEGIN
SELECT @CatId = category_id, @Merchants = merchant_name, @NotMatch = not_match, @WillMatch = will_match FROM
(
SELECT TOP (@Start + @Limit) *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS rnum
FROM filters
) a
WHERE rnum > @Start;
-- call filter procedure.
exec procSetProductCategory @CatId = @CatId, @Merchants = @Merchants, @WillMatch = @WillMatch, @NotMatch = @NotMatch;
SET @Start += 1;
SET @TotalRecords -= 1;
END
procSetProductCategory如下..
CREATE PROC [dbo].[procSetProductCategory]
(
@CatId INT = NULL,
@Merchants NVARCHAR(max),
@NotMatch NVARCHAR(max),
@WillMatch NVARCHAR(max)
)
AS
BEGIN
SET NOCOUNT ON
declare @query nvarchar(max), @orToken nvarchar(max), @andToken nvarchar(max);
set @query = 'UPDATE Products SET category_id = '+ convert(nvarchar(20), @CatId) + ' WHERE category_id IS NULL AND merchant_name IN(' + @Merchants + ')';
if(@WillMatch is not null AND LTRIM(RTRIM(@WillMatch)) != '')
BEGIN
set @andToken = '%'' AND product_name LIKE ''%';
set @WillMatch = REPLACE(@WillMatch, '+', @andToken);
set @orToken = '%'') OR (product_name LIKE ''%';
set @query = @query + ' AND ((product_name LIKE '''+ '%' + REPLACE(@WillMatch, ',', @orToken) + '%''))';
END
if(@NotMatch is not null AND LTRIM(RTRIM(@NotMatch)) != '')
BEGIN
set @andToken = '%'' AND product_name NOT LIKE ''%';
set @NotMatch = REPLACE(@NotMatch, '+', @andToken);
set @orToken = '%'') OR (product_name NOT LIKE ''%';
set @query = @query + ' AND ((product_name NOT LIKE '''+ '%' + REPLACE(@NotMatch, ',', @orToken) + '%''))';
END
EXECUTE sp_executesql @query;
END
它会生成如下的 sql 查询...
Query #1
-------------------------------------------------------------------------------------------------------
UPDATE Products SET category_id = 101 WHERE merchant_name IN('merchant 1','merchant 4','merchant 3') AND
(
(product_name LIKE '%abcd%' AND product_name LIKE '%efhg%')
) AND (
(product_name NOT LIKE '%3258%')
OR (product_name NOT LIKE '%yxzs%')
)
Query #2
-------------------------------------------------------------------------------------------------------
UPDATE Products SET category_id = 102 WHERE merchant_name IN('merchant 3', 'merchant 4') AND
(
(product_name LIKE '%1258%') OR (product_name LIKE '%abcd%')
)
注意这里使用了一些技巧。
[,] 用于区分匹配短语。 [+] 在用于两个带有 AND 条件的匹配短语的匹配字段中。
这些查询与我需要的相同..
问题是,当我使用 500 000 个产品运行此查询时,它使用了大约 100% 的 CPU。
我们如何优化不影响结果但可以降低CPU使用率的查询?
【问题讨论】:
-
你指的是哪个查询?
-
优化查询..用于填充/迭代过滤器设置和查询 procSetProductCategory..
-
您到底为什么将商家存储为逗号分隔列表?为什么不为每个商家、类别单独记录?而且我根本不理解将匹配和不匹配字段中的数据。但总的来说,您的设计有很大缺陷,这就是为什么很难查询的原因。
-
我们这里的条件很少。比如某个过滤器将应用于一组商家提供的产品。 'WillMatch' 字段表示至少有一个以逗号分隔的单词应与产品名称匹配。 'NotMatch' 表示每个单词都不应该与产品名称匹配(不应存在于产品名称中的任何位置)。两个单词与 + 相邻表示这两个单词都应该存在于产品名称中,顺序无关紧要。所以这里的并发症很少。
标签: sql sql-server performance query-optimization cpu-usage