Найти время цикла номера билета для разных интервалов - PullRequest
0 голосов
/ 14 февраля 2020
TicketNo    ActionDate                  OldStatus    NewStatus      CycleTime/Sec  
1001        2014-02-14 10:17:05.000     Assigned     InProgress     -
1001        2014-03-05 02:03:44.000     InProgress   Reply          1611999
1001        2014-03-11 10:00:14.000     Reply        Resolved       546990
1002        2015-03-20 04:44:14.000     InProgress   Reply          -
1002        2015-03-21 05:40:02.000     Reply        Resolved       89748

Я должен рассчитать время цикла для каждого изменения статуса билета.

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

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

Ответы [ 2 ]

2 голосов
/ 14 февраля 2020

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

-- Temp table with sample data
IF OBJECT_ID('tempdb..#Something') IS NOT NULL DROP TABLE #Something;
CREATE TABLE #Something
(    TicketNo INT
    , ActionDate DATETIME
    , OldStatus  VARCHAR(50)
    , NewStatus  VARCHAR(50)
);
INSERT #Something VALUES
  (1001, '2014-02-14 10:17:05.000', 'Assigned', 'InProgress')
, (1001, '2014-03-05 02:03:44.000', 'InProgress', 'Reply')
, (1001, '2014-03-11 10:00:14.000', 'Reply', 'Resolved')
, (1002, '2015-03-20 04:44:14.000', 'InProgress', 'Reply')
, (1002, '2015-03-21 05:40:02.000', 'Reply', 'Resolved')

-- TOP (1) Solution
SELECT s.TicketNo, s.ActionDate, s.OldStatus, s.NewStatus,
       CycleTimeSeconds = DATEDIFF(SECOND, MyLag.ActionDate, s.ActionDate)
FROM   #Something AS s
OUTER APPLY
(
    SELECT TOP (1) ActionDate
    FROM     #Something s2 
    WHERE    s2.TicketNo   = s.TicketNo 
    AND      s2.ActionDate < s.ActionDate
    ORDER BY s2.ActionDate DESC
) AS MyLag;

-- Using MAX instead of TOP (1) to avoid a DESC sort operation
SELECT s.TicketNo, s.ActionDate, s.OldStatus, s.NewStatus,
       CycleTimeSeconds = DATEDIFF(SECOND, MyLag.ActionDate, s.ActionDate)
FROM #Something AS s
CROSS APPLY
(
    SELECT ActionDate = MAX(ActionDate)
    FROM   #Something s2 
    WHERE  s2.TicketNo   = s.TicketNo 
    AND    s2.ActionDate < s.ActionDate 
) AS MyLag;

Поскольку подзапрос оценивает только один столбец, мы можем использовать функцию Aggregate без GROUP BY. Поскольку я использую Aggregate (MAX), я всегда получаю строку назад, поэтому я изменил OUTER APPLY на CROSS APPLY. Нет ничего плохого в OUTER APPLY, но его изменение исключает скалярный оператор из этого плана выполнения - никакого увеличения производительности, только более чистый план выполнения.

enter image description here

Существует очень большое дополнительное преимущество этого подхода: мы не только избегаем сортировки, но и избегаем сортировки DESCending . Если бы индекс был ActionDate, оптимизатор мог бы использовать его, чтобы избежать сортировки DESCending, выполнив * упорядоченное обратное сканирование. Добавление ограничения UNIQUE для ActionDate к исходной временной переменной (что я не предлагаю, но в этом примере это работает) запускает запрос TOP (1), отсортированный по ActionDate DES C.

declare @Something table
(
    TicketNo int
    , ActionDate datetime UNIQUE
    , OldStatus varchar(50)
    , NewStatus varchar(50)
)

insert @Something values
  (1001, '2014-02-14 10:17:05.000', 'Assigned', 'InProgress')
, (1001, '2014-03-05 02:03:44.000', 'InProgress', 'Reply')
, (1001, '2014-03-11 10:00:14.000', 'Reply', 'Resolved')
, (1002, '2015-03-20 04:44:14.000', 'InProgress', 'Reply')
, (1002, '2015-03-21 05:40:02.000', 'Reply', 'Resolved')

SELECT TOP (1) ActionDate
FROM     @Something AS s2 
ORDER BY s2.ActionDate DESC;

Обратите внимание на обратное сканирование:

enter image description here

Обратное сканирование убивает параллелизм. Ицик Бен-Ган обсуждает это здесь: Избежание сортировки по убыванию .

Одно из отличий между упорядоченным прямым и упорядоченным обратным сканированием состоит в том, что первое может потенциально использовать параллелизм, тогда как последнее в настоящее время не имеет реализации с параллелизмом в механизме хранения

Здесь APPLY позволяет нам выполнять операции без сортировки с параллельной обработкой в заднем кармане оптимизатора, если это необходимо. Просто еще одно доказательство того, что APPLY - это круто ... не то, чтобы нужны были какие-то дополнительные доказательства;)

1 голос
/ 14 февраля 2020

Самый простой способ сделать это - использовать LAG. Вы можете прочитать больше о функции LAG здесь. https://docs.microsoft.com/en-us/sql/t-sql/functions/lag-transact-sql?view=sql-server-ver15

Вот полностью функционирующий пример.

declare @Something table
(
    TicketNo int
    , ActionDate datetime
    , OldStatus varchar(50)
    , NewStatus varchar(50)
)

insert @Something values
(1001, '2014-02-14 10:17:05.000', 'Assigned', 'InProgress')
, (1001, '2014-03-05 02:03:44.000', 'InProgress', 'Reply')
, (1001, '2014-03-11 10:00:14.000', 'Reply', 'Resolved')
, (1002, '2015-03-20 04:44:14.000', 'InProgress', 'Reply')
, (1002, '2015-03-21 05:40:02.000', 'Reply', 'Resolved')

select s.*
    , CycleTimeSeconds = datediff(second, lag(ActionDate, 1) over(partition by TicketNo order by ActionDate), s.ActionDate)
from @Something s

- РЕДАКТИРОВАТЬ -

Здесь версия, которая будет работать с Sql Server 2008 (вам действительно следует подумать об обновлении, поскольку эта версия больше не поддерживается).

select s.*
    , CycleTimeSeconds = datediff(second, MyLag.ActionDate, s.ActionDate)
from @Something s
outer apply 
(
    select top 1 ActionDate 
    from @Something s2 
    where s2.TicketNo = s.TicketNo 
        and s2.ActionDate < s.ActionDate 
    order by ActionDate desc
) MyLag
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...