SQL суммировать мин / макс активные для временных диапазонов - PullRequest
2 голосов
/ 19 ноября 2008

Это проблема SQL, которую я не могу описать простым запросом. Возможно ли это?

Набор данных (буквы добавлены для простоты понимания):

Start      End
10:01      10:12     (A)
10:03      10:06     (B)
10:05      10:25     (C)
10:14      10:42     (D)
10:32      10:36     (E)

Желаемый вывод:

PeriodStart   New       ActiveAtEnd    MinActive   MaxActive
09:50         0         0              0           0
10:00         3 (ABC)   2 (AC)         0           3 (ABC)
10:10         1 (D)     2 (CD)         1 (C)       2 (AC or CD)
10:20         0         1 (D)          1 (C)       2 (CD)
10:30         1 (E)     1 (D)          1 (D)       2 (DE)
10:40         0         0              0           1 (D)
10:50         0         0              0           0

Таким образом, необходимый запрос представляет собой сводку первой таблицы, вычисляющую минимальные перекрывающиеся периоды времени (начало-конец) и максимальные перекрывающиеся периоды времени (начало-конец) из первой таблицы за период 10 минут.

«Новый» - это число строк с началом в итоговом периоде. «ActiveAtEnd» - это число активных строк в конце итогового периода.

Я использую Oracle, но я уверен, что решение можно изменить. Хранимые процедуры не разрешены - просто SELECT / INSERT (представления разрешены). Также можно запускать одну команду SQL на 10-минутный вывод (как только он будет заполнен, это будет соответствовать текущему состоянию.

Спасибо за любые идеи, в том числе «невозможно»; -)

Ответы [ 4 ]

3 голосов
/ 19 ноября 2008

Предполагая, что у вас также есть (или создана) таблица с именем @Times с одной записью на каждые десять минут времени начала, Как насчет ...

   Select T.Start,  
      (Select Count(*) From testTab
       Where Start Between T.Start 
          And DateAdd(minute, 10, T.Start)) New,
      (Select Count(*) From testTab
       Where Start < DateAdd(minute, 10, T.Start)
          And EndDt > DateAdd(minute, 10, T.Start)) ActiveAtEnd,
      (Select Max(Cnt) From 
          (Select Count(Distinct T.Which) Cnt
           From  (Select Distinct Start
                    From testTab
                    Where Start Between T.Start 
                           And DateAdd(minute, 10, T.Start)
                    Union Select T.Start 
                    Union Select DateAdd(minute, 10, T.Start)) Z
             Left Join testTab T 
               On Z.Start Between T.Start And T.EndDt
           Group By Z.Start) ZZ ) MaxActive,
      (Select Min(Cnt) From 
           (Select Count(Distinct T.Which) Cnt
            From  (Select Distinct Start
                   From testTab
                   Where Start Between T.Start 
                           And DateAdd(minute, 10, T.Start)
                   Union Select T.Start                                                         
                   Union Select DateAdd(minute, 10, T.Start)) Z
              Left Join testTab T 
                 On Z.Start Between T.Start And T.EndDt
           Group By Z.Start) ZZ ) MinActive     
   From @Times T

Я создал эту таблицу в SQL Server как переменную таблицы, используя

Declare @Times Table (Start datetime Primary key Not Null)
Declare @Start DateTime 
Set @Start = '1 Nov 2008 10:00'
While @Start < '1 Nov 2008 11:00' begin
    Insert @Times(Start) values(@Start)
    Set @Start = DateAdd(minute, 10, @Start) 
End

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

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

start                   endDt                   Which
----------------------- ----------------------- -----
2008-11-01 10:01:00.000 2008-11-01 10:12:00.000 A
2008-11-01 10:03:00.000 2008-11-01 10:06:00.000 B
2008-11-01 10:05:00.000 2008-11-01 10:25:00.000 C
2008-11-01 10:14:00.000 2008-11-01 10:42:00.000 D
2008-11-01 10:32:00.000 2008-11-01 10:36:00.000 E
2008-11-01 10:22:00.000 2008-11-01 10:51:00.000 F
2008-11-01 10:22:00.000 2008-11-01 10:23:00.000 G

Start                   New         ActiveAtEnd MaxActive   MinActive
----------------------- ----------- ----------- ----------- -----------
2008-11-01 10:00:00.000 3           2           3           0
2008-11-01 10:10:00.000 1           2           2           2
2008-11-01 10:20:00.000 2           2           4           2
2008-11-01 10:30:00.000 1           2           3           2
2008-11-01 10:40:00.000 0           1           2           1
2008-11-01 10:50:00.000 0           0           1           0

Предупреждение: нулевое значение устраняется с помощью агрегата или другой операции SET.

1 голос
/ 19 ноября 2008

Я борюсь со значением ActiveAtEnd, но остальные в порядке.

Это для MySQL:

set @active:=0;

select 
  period, 
  sum( if( score=1, 1, 0)) New, 
  if( max(ab) > max(aa), max(ab), max(aa)) MaxActive, 
  if( min( ab ) < min( aa ), min(ab), min(aa)) MinActive  
from  (
        select 
          period, 
          etime, 
          score, 
          @active ab, 
          @active:=@active+score aa 
        from (
               select 
                 from_unixtime( floor( unix_timestamp(start)/600) * 600) period, 
                 start etime, 
                 +1 score 
               from ev  
               union all
               select from_unixtime( floor( unix_timestamp(end)/600) * 600) period, 
                 end etime, 
                 -1 score
               from ev 
             ) event order by etime
      ) as temp 
group by period;

Самая внутренняя выборка разбивает исходную таблицу на набор событий - со счетом +1 для начального события и -1 для конечного события. union all используется для разрешения повторяющихся событий.

Следующий внутренний выбор запускает переменную между значениями оценки - @active содержит количество активных интервалов в каждый момент времени. И значение @active до и после добавления текущего счетчика выбрано: я не знаю, насколько это переносимо.

Самый внешний выбор накапливает результаты за каждый период. «Новый» - это сумма баллов «+1», MaxActive и MinActive должны учитывать значение active перед (ab) и active после (aa).

Вот примеры результатов:

+---------------------+------+-----------+-----------+
| period              | New  | MaxActive | MinActive |
+---------------------+------+-----------+-----------+
| 2008-11-19 10:00:00 |    3 |         3 |         0 |
| 2008-11-19 10:10:00 |    1 |         2 |         1 |
| 2008-11-19 10:20:00 |    0 |         2 |         1 |
| 2008-11-19 10:30:00 |    1 |         2 |         1 |
| 2008-11-19 10:40:00 |    0 |         1 |         0 |
+---------------------+------+-----------+-----------+
0 голосов
/ 19 ноября 2008

Единственный способ, которым я когда-либо смог решить эту проблему, - это получить счетчик «start» за каждый минутный период. Затем вы получите максимум (или минимум) для 10-минутной группы. Я не смог применить подход, основанный на множествах.

0 голосов
/ 19 ноября 2008

New и ActiveAtEnd довольно просты (при условии, что начало и конец периода хранятся во временных переменных):

select  @periodStart PeriodStart
, @periodEnd PeriodEnd 
, n.[new]
, ae.ActiveAtEnd
from (
select  count(*) [new] 
from    @times 
where   [start] >= @periodStart
and [start] < @PeriodEnd 
) n 
cross join 
(
select  count(*) [ActiveAtEnd] 
from    @times
where   [start] < @PeriodEnd 
and [end] >= @PeriodEnd 
) ae

Макс и Мин Актив сложнее. Вы можете предположить, что степень детализации составляет минуту, поэтому вам нужно будет выделить активный период с такой степенью детализации, чтобы иметь возможность исследовать каждый срез.

Я не уверен, что это возможно в одном запросе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...