SQL Server - агрегирование SUM для каждой строки в таблице с несколькими условиями - PullRequest
0 голосов
/ 28 декабря 2018

Я пытаюсь смоделировать что-то, что я делаю в Excel довольно часто в SQL, однако я, должно быть, делаю что-то не так, потому что это привязало ЦП сервера к 100.

Что я конкретно пытаюсь сделать, это агрегировать суммудля каждой строки в таблице на основе определенных условий.Запрос ниже

  SELECT  custno,count(custno) as countUse into ##AllTransferMembers
  FROM [OLBRET_COPY].[dbo].[ActivityLogs]
  where ActivityCode in ('11020','11045','11053') 
    and DateTime >= '2018-12-20' and DateTime < '2018-12-26' --and ErrorCode in ('')
  group by custno   

  select d.custno, ActivityCode, d.Amount, d.DateTime, a.countUse, MobileDeviceMACId,ErrorMessage
    ,( select SUM(activitylogs.amount) from [OLBRET_COPY].[dbo].[ActivityLogs] where ActivityLogs.DateTime >= (d.DateTime-2) and ActivityLogs.DateTime <= d.DateTime and ActivityLogs.CustNo = d.CustNo and ActivityLogs.ErrorMessage = '')
    FROM [OLBRET_COPY].[dbo].[ActivityLogs] d
    inner join ##AllTransferMembers a on d.CustNo = a.CustNo
    where ActivityCode in ('11020','11045','11053') 
        and DateTime >= '2018-12-20' and DateTime < '2019-01-01' --and ErrorCode in ('') 
    order by CustNo,DateTime

    drop table ##AllTransferMembers

Проблема должна заключаться в агрегации суммы подзапроса, потому что другая часть запроса выполняется довольно часто в другом параметре без каких-либо проблем.

(select 
SUM(activitylogs.amount) 
from [OLBRET_COPY].[dbo].[ActivityLogs] 
where ActivityLogs.DateTime >= (d.DateTime-2) and ActivityLogs.DateTime <= d.DateTime 
and ActivityLogs.CustNo = d.CustNo 
and ActivityLogs.ErrorMessage = '')

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

Приветствия,

Крис

Ответы [ 2 ]

0 голосов
/ 31 декабря 2018

Используя всеобщую помощь и некоторое прибегая к помощи, это был мой последний запрос:

create index [ix_ActLogstmp] on [dbo].[allDataTodorov]
 (CustNo, ErrorMessage,DateTime) include (Amount);


 With tmp_AllTransferMembers as (
 Select 
 custno, 
 count(custno) as countUse
 from [SegmentationDatamart].[dbo].[allDataTodorov]
 where ActivityCode in ('11020','11045','11053') 
    and [DateTime] >= '2018-08-01' and DateTime < '2018-12-26'
group by custno)



select 
d.custno, 
ActivityCode, 
d.Amount, 
d.DateTime,
a.countUse, 
MobileDeviceMACId,
ErrorMessage, 
( select SUM([allDataTodorov].amount) 
      from [SegmentationDatamart].[dbo].[allDataTodorov]
      where [allDataTodorov].DateTime >= (d.DateTime-2) 
      and [allDataTodorov].DateTime <= d.DateTime 
      and [allDataTodorov].CustNo = d.CustNo 
      and [allDataTodorov].ErrorMessage is null) as [SUM_]

FROM [SegmentationDatamart].[dbo].[allDataTodorov] d
    inner join tmp_AllTransferMembers a on d.CustNo = a.CustNo
    where d.ActivityCode in ('11010','11011','11020','11045','11053') 
        and d.DateTime >= '2018-08-01' 
        and d.DateTime < '2018-12-26'
  order by d.CustNo,d.DateTime

  drop index [ix_ActLogstmp]

Некоторые примечания:

  1. Мне удалось получить план выполнения через'Activity Monitor' => 'Недавние дорогие запросы'.Поскольку поле «Количество» не было в индексе, SQL выполнял поиск кластеризованного ключа и сортировку, которая занимала 97% времени выполнения для запроса.

  2. Из-за отсутствия разрешений на сервере мне пришлось перенести данные на другой сервер, где я мог создать индекс с полем «Количество».Запрос выполнялся примерно за 5 секунд с 600K строк.

0 голосов
/ 28 декабря 2018

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

CREATE INDEX IX_AllTransferMembers ON ##AllTransferMembers (CustNo)

В любом случае вы можете сделать то же самое с подзапросом, используя CTE, например:

WITH tmp_AllTransferMembers AS (
  SELECT  custno,count(custno) as countUse 
  FROM [OLBRET_COPY].[dbo].[ActivityLogs]
  where ActivityCode in ('11020','11045','11053') 
    and DateTime >= '2018-12-20' and DateTime < '2018-12-26' 
  group by custno) 
SELECT d.custno, ActivityCode, d.Amount, d.DateTime, 
     a.countUse, MobileDeviceMACId,ErrorMessage
    ,( select SUM(activitylogs.amount) 
       from [OLBRET_COPY].[dbo].[ActivityLogs] 
       where ActivityLogs.DateTime >= (d.DateTime-2) 
       and ActivityLogs.DateTime <= d.DateTime 
       and ActivityLogs.CustNo = d.CustNo 
       and ActivityLogs.ErrorMessage = '')
    FROM [OLBRET_COPY].[dbo].[ActivityLogs] d
    inner join tmp_AllTransferMembers a on d.CustNo = a.CustNo
    where d.ActivityCode in ('11020','11045','11053') 
        and d.DateTime >= '2018-12-20' 
        and d.DateTime < '2019-01-01' --and ErrorCode in ('') 
  order by d.CustNo,d.DateTime

Вы должны убедиться, чточто у вас есть индекс ActivityLogs (CustNo, ErrorMessage, DateTime). Как вы заявили, вы не можете видеть план и воспроизводить его, поэтому непросто убедиться, что он будет работать лучше ...

Кроме того, предыдущийк вашему коду вы можете добавить две строки ниже, чтобы показать некоторые детали того, где это занимает время.Поскольку это отдельная таблица, она будет непростой, но может дать вам некоторую подсказку (подробности см. На вкладке «Сообщение» после выполнения запроса):

SET STATISTICS TIME ON;
SET STATISTICS IO ON;
...