2017-02-03 18 views
1

я пишу SQL запросов для отображения различных типов результатов согласно следующим ТРЕБОВАНИЯМ:Sql Server - самый быстрый способ получить количество раз значение изменилось для каждого ID

  1. Дисплей записи, который значение изменилось с 1 до 0, максимальное время в заданном временном метки
  2. записи Показать, кто значение изменяется от 1 до 0, минимального времени в данном временной метке
  3. Показать 10 лучшие записей, который значение изменилось с 1 до 0 , Максимальное время в заданной временной метки

Образец данных:

 
+----------+-------------+-------------+ 
| DeviceId | CaptureTime | SensorValue | 
+----------+-------------+-------------+ 
| DC001 | 02/01/2017 |  0  | 
| DC001 | 02/02/2017 |  1  | 
| DC001 | 02/03/2017 |  0  | 
| DC001 | 02/04/2017 |  1  | 
| DC001 | 02/05/2017 |  0  | 
| DC001 | 02/07/2017 |  1  | 
| DC001 | 02/08/2017 |  0  | 
| DC001 | 02/10/2017 |  1  | 
| DC001 | 02/01/2017 |  0  | 
| DC001 | 02/01/2017 |  0  | 
| DC002 | 02/02/2017 |  1  | 
| DC002 | 02/02/2017 |  0  | 
| DC002 | 02/02/2017 |  1  | 
| DC002 | 02/02/2017 |  1  | 
| DC002 | 02/02/2017 |  1  | 
| DC002 | 02/03/2017 |  1  | 
| DC002 | 02/03/2017 |  0  | 
| DC002 | 02/03/2017 |  0  | 
| DC002 | 02/03/2017 |  1  | 
| DC002 | 02/03/2017 |  1  | 
| DC003 | 02/03/2017 |  1  | 
| DC003 | 02/03/2017 |  1  | 
| DC003 | 02/03/2017 |  0  | 
| DC003 | 02/03/2017 |  1  | 
| DC003 | 02/03/2017 |  1  | 
| DC003 | 02/04/2017 |  1  | 
| DC003 | 02/05/2017 |  1  | 
| DC003 | 02/06/2017 |  1  | 
| DC003 | 02/07/2017 |  1  | 
| DC003 | 02/08/2017 |  1  | 
| DC004 | 02/09/2017 |  0  | 
| DC004 | 02/10/2017 |  0  | 
| DC004 | 02/11/2017 |  1  | 
| DC004 | 02/12/2017 |  0  | 
| DC004 | 02/12/2017 |  1  | 
| DC004 | 02/12/2017 |  1  | 
| DC004 | 02/12/2017 |  1  | 
| DC004 | 02/12/2017 |  1  | 
| DC004 | 02/12/2017 |  1  | 
| DC004 | 02/12/2017 |  1  | 
| DC005 | 02/12/2017 |  0  | 
| DC005 | 02/12/2017 |  0  | 
| DC005 | 02/12/2017 |  0  | 
| DC005 | 02/12/2017 |  0  | 
| DC005 | 02/14/2017 |  0  | 
| DC005 | 02/14/2017 |  0  | 
| DC005 | 02/14/2017 |  0  | 
| DC005 | 02/14/2017 |  0  | 
| DC005 | 02/14/2017 |  0  | 
| DC005 | 02/14/2017 |  0  | 
+----------+-------------+-------------+ 

Я создал ниже общий запрос для всех трех требований:

DECLARE @HoursBack INT 
     , @MinMax VARCHAR(3) 
     , @TopRows INT 

SELECT TOP (@TopRows) COUNT(TD1.DeviceId) PickedNoOfTimes, ItemName -- I have removed table to get ItemName to simplify this query 
FROM tTrayDetails AS TD1 
WHERE TD1.SensorValue = 0 
AND  TD1.CaptureTime > DATEADD(HOUR, [email protected], GETDATE()) 
AND  TD1.SensorValue <> (
           SELECT TOP 1 SensorValue 
           FROM tTrayDetails TD2 
           WHERE TD2.CaptureTime < TD1.CaptureTime 
           ORDER BY TD2.CaptureTime DESC 
          ) 
GROUP BY TD1.DeviceId 
ORDER BY CASE WHEN @MinMax = 'Max' THEN COUNT(TD1.DeviceId) END DESC 
      , CASE WHEN @MinMax = 'Min' THEN COUNT(TD1.DeviceId) END ASC 

Этот запрос работает для всех трех требований, путем просто устанавливать различные значения @HoursBack, @MinMax и @TopRows.

Здесь значения установить для моих трех требований:

  1. @HoursBack = 24, @ MinMax = 'Макс', @ TopRows = 1
  2. @HoursBack = 24, @ MinMax = 'Min', @ TopRows = 1
  3. @HoursBack = 24, @ MinMax = 'Макс', @ TopRows = 10

Теперь Проблема: Этот запрос занимает около 40 сек для выполнения, только для 14K записей в тестовой среде.

В производственной среде ежедневные записи 2-4K будут добавлены, поэтому время выполнения этого запроса будет увеличиваться.

Как я могу изменить запрос, чтобы быстрее работать с большим объемом данных.

+0

Посмотрите на функцию 'LAG() OVER()', с помощью этой функции вы можете выбрать предыдущее значение и определить, является ли данная строка изменением или нет. – Aquillo

+0

Не должны ли данные вашего образца включать время? – SqlZim

+0

Что такое выпуск SQL Server? 2012 поддерживает LAG/LEAD. – dnoeth

ответ

2

Это будет рассчитывать только те строки, в которых SensorValue изменились с 1 до 0:

WITH cte AS 
(
    SELECT DeviceId, 
     -- previous row = 1 and current row = 0 
     CASE WHEN LAG(SensorValue) 
       Over (PARTITION BY DeviceId 
         ORDER BY CaptureTime) = 1 
       AND SensorValue = 0 
      THEN 1 
      ELSE 0 
     END AS ChangeFlag 
    FROM tTrayDetails AS t 
    WHERE .... 
) 
SELECT DeviceId, Count(*) 
FROM cte 
WHERE ChangeFlag = 1 
GROUP BY DeviceId 

Теперь примените ваши TOP/ORDER BY ...

+0

Спасибо @dnoeth это действительно работает, как шарм! – Shri

1

Вот оно:

declare 
    @topRows int = 2, 
    @minMax nvarchar(3) = 'max', 
    @hoursBack int = 1000, 
    @now datetime = getdate(); 

;with _raw 
as (
    select 
     DeviceId, 
     case when SensorValue = 0 and lag(SensorValue) over (partition by DeviceId order by CaptureTime) = 1 
      then 1 
      else 0 
     end as Val 
    from tTrayDetails 
    where 
     CaptureTime > dateadd(hour, [email protected], @now) 
) 
, _combined 
as (
    select 
     DeviceId, 
     sum(Val) as Val, 
     (case when @minMax = 'min' then 1 else -1 end) * sum(Val) as Ord 
    from _raw 
    group by 
     DeviceId 
) 
select top(@topRows) 
    DeviceId, Val 
from _combined 
order by 
    Ord, DeviceId 

И то же самое, что и тестовый сценарий:

create table #tTrayDetails 
(
    DeviceId nvarchar(128), 
    CaptureTime datetime not null, 
    SensorValue int not null 
) 

insert into #tTrayDetails(DeviceId, CaptureTime, SensorValue) values 
    ('DC001', '2017-01-01 01:00:00', 0), 
    ('DC001', '2017-01-01 02:00:00', 1), 
    ('DC001', '2017-01-02 01:00:00', 0), 
    ('DC001', '2017-01-03 01:00:00', 1), 
    ('DC001', '2017-01-04 01:00:00', 0), 
    ('DC002', '2017-01-01 01:00:00', 0), 
    ('DC002', '2017-01-01 02:00:00', 0), 
    ('DC002', '2017-01-01 03:00:00', 1), 
    ('DC002', '2017-01-01 04:00:00', 1), 
    ('DC002', '2017-01-01 05:00:00', 1), 
    ('DC002', '2017-01-01 06:00:00', 0), 
    ('DC003', '2017-01-01 06:00:00', 0) 

declare 
    @topRows int = 2, 
    @minMax nvarchar(3) = 'max', 
    @hoursBack int = 1000, 
    @now datetime = getdate(); 

;with _raw 
as (
    select 
     DeviceId, 
     case when SensorValue = 0 and lag(SensorValue) over (partition by DeviceId order by CaptureTime) = 1 
      then 1 
      else 0 
     end as Val 
    from #tTrayDetails 
    where 
     CaptureTime > dateadd(hour, [email protected], @now) 
) 
, _combined 
as (
    select 
     DeviceId, 
     sum(Val) as Val, 
     (case when @minMax = 'min' then 1 else -1 end) * sum(Val) as Ord 
    from _raw 
    group by 
     DeviceId 
) 
select top(@topRows) 
    DeviceId, Val 
from _combined 
order by 
    Ord, DeviceId 


drop table #tTrayDetails 
+0

Спасибо @pkuderov .. Это хорошее решение, но я использовал решения dnoeth, поскольку он работает так же, как и с меньшим количеством скриптов. Но ваше решение действительно помогло мне в другом сценарии, потому что оно возвращает группы с измененными значениями, а также возвращает группы с изменить счетчик '0'. Косвенно вы помогли мне в двух запросах .... – Shri

+0

@ShrikantBhusalwad приветствуется :) Выберите решение dnoeth как «Accepted», pls. – pkuderov