Оптимизация медленного выполнения запроса с наибольшим числом групп - PullRequest
0 голосов
/ 04 июля 2018

У меня есть следующая таблица:

id | query   | update_date | website_id | device  | page    | impressions | clicks | position | is_brand
---+---------+-------------+------------+---------+---------+-------------+--------+----------+---------
1  | kitchen | 2018-05-01  | 2          | desktop | http... | 11000       | 50     | 3        | 1
2  | table   | 2018-05-01  | 2          | desktop | http... | 7000        | 40     | 3        | 0
3  | kitchen | 2018-05-02  | 2          | desktop | http... | 11500       | 55     | 3        | 1
4  | table   | 2018-05-02  | 2          | desktop | http... | 7100        | 35     | 3        | 0

В этой таблице мне нужна процедура, которая для каждого уникального запроса дает мне наиболее эффективную строку в отношении кликов за данный период времени. Это привело к следующей процедуре:

create or alter procedure get_best_website_querys
    @from as date,
    @to as date,
    @website_id as int
as
begin
    WITH    cte
          AS (SELECT    *
              ,         ROW_NUMBER() OVER (PARTITION BY query ORDER BY clicks DESC) RN
              FROM      search_console_query
              where 
                update_date >= @from and 
                update_date <= @to and 
                website_id = @website_id 
             )
    SELECT  cte.id
     ,      cte.query
     ,      cte.update_date
     ,      cte.website_id
     ,      cte.device
     ,      cte.page
     ,      cte.impressions
     ,      cte.clicks
     ,      cte.POSITION
     ,      cte.is_brand
    FROM    cte
    WHERE   RN = 1
end;

Теперь это работает и дает мне правильный результат. Моя проблема в том, что эта таблица становится довольно большой, и этот запрос выполняется довольно медленно (> 3 минуты в год). Запрос дает следующий план выполнения:

enter image description here

В таблице у меня есть некластеризованный индекс на clicks и кластеризованный на (website_id, update_date).

Я хотел бы получить некоторую информацию о том, как лучше всего добиться того, чтобы это работало лучше. Любой вклад будет оценен.

Ответы [ 4 ]

0 голосов
/ 04 июля 2018

Альтернативный способ сделать это. Но не уверен насчет производительности, обычно этот шаблон используется для поиска последней записи с несгруппированными столбцами в списке выбора.

SELECT          a.id,
                a.query,
                a.update_date,
                a.website_id,
                a.device,
                a.page,
                a.impressions,
                a.clicks,
                a.POSITION,
                a.is_brand
FROM            search_console_query a
LEFT JOIN       search_console_query b  ON b.website_id = a.website_id
                                       AND a.query = b.query
                                       AND a.clicks > b.clicks
WHERE           update_date >= @from
                AND update_date <= @to
                AND website_id = @website_id
                AND b.clicks IS NULL
0 голосов
/ 04 июля 2018

Я бы порекомендовал использовать индексы, предложенные выше. Во-вторых, здесь также может происходить сниффинг параметров. Я бы посоветовал вам повторно объявить переменные внутри хранимой процедуры следующим образом, чтобы сниффинг параметров не происходил:

create or alter procedure get_best_website_querys    
    @from as date,
    @to as date,
    @website_id as int
as
begin
DECLARE @StartDate AS DATE = @from
       ,@EndDate AS DATE = @to
       ,@WebsiteID AS INT = @website_id

      WITH    cte
      AS (SELECT    *
          ,         ROW_NUMBER() OVER (PARTITION BY query ORDER BY clicks DESC) RN
          FROM      search_console_query
          where 
            update_date >= @StartDate and 
            update_date <= @EndDate and 
            website_id = @WebsiteID
         )
SELECT  cte.id
 ,      cte.query
 ,      cte.update_date
 ,      cte.website_id
 ,      cte.device
 ,      cte.page
 ,      cte.impressions
 ,      cte.clicks
 ,      cte.POSITION
 ,      cte.is_brand
FROM    cte
WHERE   RN = 1
end;
0 голосов
/ 04 июля 2018

Похоже, что все столбцы в предложении select indexable ; Вы можете попробовать создать массивный индекс покрытия, используя включенные столбцы:

CREATE INDEX TEST_0001 ON search_console_query (
    website_id,
    update_date,
    query,
    clicks
) INCLUDE (
    id,
    device,
    page,
    impressions,
    position,
    is_brand
)

Пока вы это делаете, попробуйте больше вариантов ниже и посмотрите, какой из них выбран SQL-сервером, а затем удалите ненужные:

  • изменение порядка website_id, update_date
  • перемещение query, clicks внутри включенных столбцов
0 голосов
/ 04 июля 2018

Сначала попробуйте добавить индекс на search_console_query scq(website_id, update_date, query, clicks).

Тогда предложите попробовать эту версию:

select scq.*
from search_console_query scq
where scq.update_date >= @from and 
      scq.update_date <= @to and 
      scq.website_id = @website_id and
      scq.clicks = (select max(scq2.clicks)
                    from search_console_query scq2
                    where scq2.website_id = scq.website_id and
                          scq2.query = scq.query and
                          scq2.update_date >= @from and
                          scq2.update_date <= @to
                    );

Эта версия может использовать два индекса: search_console_query(website_id, query, update_date, clicks) и search_console_query(website_id, update_date, query, clicks).

Это немного отличается, потому что он возвращает несколько строк для запроса в случае связей. Если производительность значительно улучшится - и это проблема, - это можно исправить.

EDIT:

Самый простой способ удалить дубликаты для второй версии - предположить, что таблица имеет уникальный столбец id:

select scq.*
from search_console_query scq
where scq.update_date >= @from and 
      scq.update_date <= @to and 
      scq.website_id = @website_id and
      scq.sqc_id = (select top (1) sqc2.id
                    from search_console_query scq2
                    where scq2.website_id = scq.website_id and
                          scq2.query = scq.query and
                          scq2.update_date >= @from and
                          scq2.update_date <= @to
                    order by scq2.clicks desc);
...