Конференция "Базы" » SQL запрос, сумма проммежутков времени. Как убрать пересечения? [MSSQL]
 
  • Иксик © (09.12.12 01:00) [0]
    Вот грубо говоря такая таблица: ID  StartedAt, EndedAt
    И грубо говоря по ней делается примерно такой запрос:
    SUM(DATEDIFF(SECOND, [StartedAt], [EndedAt])).

    Возникла ситуация, когда могут перекрываться промежутки, т.е. StartedAt больше, чем EndedAt в предыдущей строке. Либо у меня голова отказывается варить в субботу вечером, либо просто не существует способа извлечь сумму минус перекрывающиеся промежутки одним запросом. Обрабатывать запись за записью стремновато, т.к. их много-много... Может у кого-нибудь возникнет светлая идея?
  • Cobalt © (10.12.12 13:11) [1]
    Представь себе:
    1 - 01.12 - 10.12 - 1600
    2 - 05.12 - 12.12 - 1200

    Вопрос: какая сумма должна быть за период со 2 по 7-ое, и главное, почему?
    Правила сначала определи, как разделять сумму по датам.
  • O'ShinW © (10.12.12 15:09) [2]
    причем тут числа?

    т.е. зачем так?

    > Представь себе:
    > 1 - 01.12 - 10.12 - 1600
    > 2 - 05.12 - 12.12 - 1200


    так надо
    > 1 - 01.12 - 10.12
    > 2 - 05.12 - 12.12
    ну а сумма - очевидна: 12 дней
  • Иксик © (10.12.12 15:11) [3]
    Не понял что значит 1600 и 1200. Там полный timestamp хранится. Сумма будет равна количеству секунд между (01.12-12.12), т.е. сумма двух промежутков, минус их пересечение.
  • Иксик © (10.12.12 15:13) [4]
    Единственное, что приходит в голову - это поставить триггер на добавление записи и если StartedAt меньше последнего EndedAt, то делать его равным последнему EndedAt. Тогда запрос будет тривиальным.
  • O'ShinW © (10.12.12 15:14) [5]
    т.е., коль скоро дата суть число:
    на параллельных прямых к оси OX есть отрезки
    требуется найти сумму длин их проекций на OX
  • Sergey13 © (10.12.12 15:34) [6]

    > Сумма будет равна количеству секунд между (01.12-12.12),
    >  т.е. сумма двух промежутков, минус их пересечение.

    А
    Max([EndedAt]) - Min([StartedAt])
    не то?
  • Иксик © (10.12.12 15:39) [7]

    >  Sergey13 ©   (10.12.12 15:34) [6]


    Нет, ведь между ними могут быть незаполненные промежутки, т.е. например:
    1. 01.12.2012 - 03.12.2012
    2. 05.12.2012 - 07.12.2012
  • Anatoly Podgoretsky © (10.12.12 16:14) [8]

    > Иксик ©   (09.12.12 01:00)  

    А зачем запросами, всего один запрос, а потом проход по датасет, очень быстро.
  • O'ShinW © (10.12.12 16:21) [9]
    аля так посчитать пересечения, потом отнять от исходного

    --сырьё
    insert into TT
    select * from (
    select CONVERT(datetime, '01.01.2012') F, CONVERT(datetime, '03.01.2012') S
    union select CONVERT(datetime, '12.01.2012'), CONVERT(datetime, '18.01.2012')
    union select CONVERT(datetime, '15.01.2012'), CONVERT(datetime, '16.01.2012')
    union select CONVERT(datetime, '17.01.2012'), CONVERT(datetime, '22.01.2012')
    ) A
    --сырьё

    --запрос
    select
    *,
    case
     when (F1 between f2 and s2) and (S1 between f2 and s2) then DATEDIFF(DAY, [F1], [S1])
     when (F1 between f2 and s2) then  DATEDIFF(DAY, [F1], [s2])
     when (S1 between f2 and s2) then  DATEDIFF(DAY, [f2], [S1])
     else 0
    end
    from (
    select T1.F f1, T1.S s1, T2.F f2, T2.S s2
    from TT T1
    full join TT T2 on T1.F <> T2.F and T1.S <> T2.S --построить почти :) полное произведение
    ) A
    --запрос
  • Иксик © (10.12.12 18:00) [10]

    > Anatoly Podgoretsky ©   (10.12.12 16:14) [8]
    >
    >
    > > Иксик ©   (09.12.12 01:00)  
    >
    > А зачем запросами, всего один запрос, а потом проход по
    > датасет, очень быстро.


    Будет быстро? Там до 10 миллионов записей за раз может быть... А насчет триггеров вариант не нравится?
  • Иксик © (10.12.12 18:01) [11]

    >  O'ShinW ©   (10.12.12 16:21) [9]


    Но там не дефинированное количество записей.
  • знайка (10.12.12 18:56) [12]
    А зачем тогда триггер, сразу пишите то что надо.
  • Кщд (11.12.12 10:10) [13]
    Надо перепипежить для MS, но идея и так понятна:

    with g as (
             select rownum rn, dt, dt2
             from  (
                   select to_date('01.01.2012') dt, to_date('03.01.2012')  dt2 from dual
                   union all
                   select to_date('12.01.2012'), to_date('18.01.2012') from dual
                   union all
                   select to_date('15.01.2012'), to_date('16.01.2012') from dual
                   union all
                   select to_date('17.01.2012'), to_date('22.01.2012') from dual
                   union all
                   select to_date('29.12.2011'), to_date('28.01.2012') from dual                
                   )
             )
    select j.dt,
          max(dt2) dt2
    from
       (
       select g.rn,
              min(least(g.dt, g2.dt)) dt,
              max(greatest(g.dt2, g2.dt2)) dt2
       from g g
              join g g2
                on g.dt between g2.dt and g2.dt2
                   or g.dt2 between g2.dt and g2.dt2
       group by g.rn  
       ) j
    group by j.dt        



    Для 1e7 записей будет нескоро, даже при наличии соответствующих индексов.
  • O'ShinW © (12.12.12 13:33) [14]

    > Кщд   (11.12.12 10:10) [13]
    > with g as (


    Это таблица, к которой потом если обращаться, она не будет каждый раз пересчитываться. Правильно?
  • O'ShinW © (12.12.12 13:40) [15]

    > Но там не дефинированное количество записей.

    ну и что?
    там для исходных данных у меня несколько примеров просто было

    Но больше понравился вариант  Кщд  
             join g g2
               on g.dt between g2.dt and g2.dt2
                  or g.dt2 between g2.dt and g2.dt2

    сразу пересекающиеся
    и потом пересечения
  • Кщд (13.12.12 08:05) [16]
    >O'ShinW ©   (12.12.12 13:33) [14]
    материализовать(создать временную таблицу) или использовать inline(аналогия с inline в Delphi - корректна) решает оптимизатор СУБД. в Oracle существует возможность явного управления этим поведением посредством "подсказок"(hints) оптимизатору. как в MS, увы, не знаю.
  • O'ShinW © (13.12.12 10:09) [17]

    > Кщд   (13.12.12 08:05) [16]

    спасибо

    Прошу прощения у автора ветки,
    но еще раз хочу спросить

    допустим, тяжелый запрос SQ (с группировками), вернул аля так
    NAME(varchar2) VAL(number)
    NAME1  VAL1
    NAME1  VAL2
    NAME1  VAL3
    NAME2  VAL1
    NAME2  VAL3

    а теперь надо еще посчитать count(name) и получить аля так
    NAME(varchar2) VAL(number)  CNT(number)
    NAME1  VAL1 3    --3, потому что NAME1 присутствует 3 раза
    NAME1  VAL2 3
    NAME1  VAL3 3
    NAME2  VAL1 2     --2, потому что NAME2 присутствует 2 раза
    NAME2  VAL3 2

    т.е. этот запрос SQ придется еще раз группировать, но тогда потеряется VAL

    если написать аля
    with G as ( SQ )
    select
      G.NAME, G.VAL, G2.CNT
    from
       G
       join  ГРУППИРОВКА_ПО_NAME(G) G2 on G.NAME = G2.NAME
    тогда не будет по идее два раза тяжелый запрос выполнятся?

    зы
    Или как-то лучше можно решить?
  • Кщд (13.12.12 14:05) [18]
    O'ShinW ©   (13.12.12 10:09) [17]
    как-будто, с 2005-го появились аналитические ф-ции)
  • Игорь Шевченко © (13.12.12 22:17) [19]
    O'ShinW ©   (13.12.12 10:09) [17]

    ROLLUP ?
 
Конференция "Базы" » SQL запрос, сумма проммежутков времени. Как убрать пересечения? [MSSQL]
Есть новые Нет новых   [119569   +90][b:0][p:0.002]