Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/85.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQL SERVER-查询优化&x27;比如';导致大多数cpu使用率为100%_Sql_Sql Server_Performance_Query Optimization_Cpu Usage - Fatal编程技术网

SQL SERVER-查询优化&x27;比如';导致大多数cpu使用率为100%

SQL SERVER-查询优化&x27;比如';导致大多数cpu使用率为100%,sql,sql-server,performance,query-optimization,cpu-usage,Sql,Sql Server,Performance,Query Optimization,Cpu Usage,我在数据库产品和过滤器中有两个表 模式: 我创建了一个查询,从filters表中查找所有记录,循环每个记录,并调用一个为Products表设置category id的过程 过滤表数据如下所示 过滤器选择查询如下所示 DECLARE @TotalRecords INT, @Start INT, @Limit INT, @CatId INT, @Merchants NVARCHAR(max), @NotMatch NVARCHAR(max), @WillMatch NVARCHAR(max);

我在数据库产品过滤器中有两个表

模式:

我创建了一个查询,从filters表中查找所有记录,循环每个记录,并调用一个为Products表设置category 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
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
procSetProductCategory如下所示

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
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%')
)
注意这里使用了一些技巧

[,]用于区分匹配短语。 [+]在匹配字段中,用于具有和条件的两个匹配短语

这些查询与我所需要的相同

问题是,当我用500000个产品运行这个查询时,它使用了大约100%的CPU


我们如何优化不影响结果但可以减少CPU使用的查询?

如果没有查询计划,很难确定,但我猜这是因为您正在匹配
'%something%'
,这意味着查询必须检查每一行

这总是很慢,而且在索引方面你也无能为力


如果您正在进行文本比较,那么使用SQL Server的功能可能会获得更好的性能

首先,正如已经指出的那样:这里的逻辑确实有问题。这就是说,假设你被它困住了,你可能会想尝试一些事情。 我的第一个问题是:这个东西能运行多久?你不应该太担心它需要100%的CPU;问题是要花多少时间才能完成

问题1: 似乎您正在
过滤器
表上创建一个循环,逐个获取每一行

  • SQL没有被优化为执行逐行操作;您确实应该考虑将逻辑更改为基于
  • 的集合。
  • 如果您确实想逐行执行某项操作,请使用
    光标
    ,而不是当前方法。
    • 首先,检查整个表,计算有多少个过滤器
    • 然后浏览整个表并按
      选择1
    • 从已排序的列表中,您选择一个
      rnum
      大于计数器的
=>这在很多方面都是错误的,实际上很痛=(

  • 如果按
    选择1
    进行排序/排序,则它可以按第一次ABCD和第二次BADC的顺序返回记录;这两个答案都是正确的,因为您是按常量排序的:记录的实际顺序无关紧要
  • 每次执行循环时,服务器都必须对整个表进行排序,然后才能判断哪些
    rnum
    值符合大于
    @start
    的要求;每次
  • 将有许多记录适合
    rnum>@start
    ,用于填充记录的返回记录可以是其中任何一个
要“修复”此问题,我建议使用以下方法:

DECLARE @TotalRecords INT, 
        @Start INT, 
        @Limit INT, 
        @CatId INT, 
        @Merchants NVARCHAR(max), 
        @NotMatch NVARCHAR(max), 
        @WillMatch NVARCHAR(max);

DECLARE filter_loop CURSOR LOCAL FAST_FORWARD
    FOR SELECT category_id, 
               merchant_name,
               not_match,
               will_match
          FROM filters
         ORDER BY id -- not required but makes debugging easier
OPEN filter_loop 
FETCH NEXT FROM filter_loop INTO @CatId, @Merchants, @NotMatch, @WillMatch
WHILE @@FETCH_STATUS = 0
    BEGIN

        -- call filter procedure.
        exec procSetProductCategory @CatId = @CatId, @Merchants = @Merchants, @WillMatch = @WillMatch, @NotMatch = @NotMatch;

        -- get next filter
        FETCH NEXT FROM filter_loop INTO @CatId, @Merchants, @NotMatch, @WillMatch
    END
CLOSE filter_loop 
DEALLOCATE filter_loop 
问题2: 乍一看,我对存储过程本身几乎无能为力。有一些动态sql字符串构建可能会稍微优化一下,但我非常怀疑它会产生多大影响。因为它现在是相当可读的,所以我将保持原样。 生成的查询实际上如下所示:

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%'))
我建议为其创建以下索引:

CREATE INDEX idx_test ON Products (merchant_name) INCLUDE product_name)
事后思考
即使进行了上述更改,在处理100k+记录时,此操作仍将运行相当长的一段时间。解决此问题的唯一真正方法是使用基于集合的方法,但需要庞大的动态sql字符串;或者对数据本身有更好的了解。例如,您可以尝试组合不同的
过滤器
具有相同
值但不同
匹配
/
NoMatch
值的记录可能不太难,但我建议先从上面的建议开始,然后看看最终结果。

您指的是哪一个查询?优化两个查询..用于计算/迭代过滤器设置和d对procSetProductCategory的查询..你究竟为什么要将商户存储为逗号分隔的列表?为什么不为每个商户、类别单独记录?我根本不理解will match和not match字段中的数据。但总的来说,你的设计是非常有缺陷的,这就是为什么很难查询的原因。我们这里没有几个条件。比如某些筛选器将应用于一组商家提供的产品。“WillMatch”字段表示至少有一个由逗号分隔的单词应与产品名称匹配。“NotMatch”表示每个单词都不应与产品名称匹配(不应存在于产品名称内的任何位置).两个词与+相邻表示这两个词都应该存在于产品名称顺序中并不重要。因此,这里没有太多复杂之处。我也尝试过。但有一些问题,因为我需要与否定和肯定短语(will_match,not_match)进行标记匹配.谢谢分享你的想法。这很有帮助。我正在按照你的建议寻找解决方案。