Запрос Oracle SELECT: сворачивание значений NULL при объединении дат в разные поля - PullRequest
0 голосов
/ 23 июня 2010

Этот вопрос очень похож на мой предыдущий вопрос , но немного сложнее. Ответ Роба ван Вейка отлично сработал для другого моего вопроса, и я использовал его в качестве отправной точки.Моя проблема сейчас в том, что я устанавливаю даты для разных полей.Если раньше я заботился о получении всех значений open_in и open_out для данного id, то теперь я хочу new_in, new_out, open_in, open_out, fixed_in и fixed_out длякаждый id.У меня есть следующее:

SELECT id,
       state,
       state_time,
       MAX(new_row_num) OVER (PARTITION BY id ORDER BY state_time) AS new_row_group,
       MAX(open_row_num) OVER (PARTITION BY id ORDER BY state_time) AS open_row_group,
       MAX(fixed_row_num) OVER (PARTITION BY id ORDER BY state_time) AS fixed_row_group
FROM (
       SELECT id,
              state,
              state_time,
              CASE state
                   WHEN 'New'
                   THEN ROW_NUMBER() OVER (PARTITION BY id ORDER BY state_time)
              END AS new_row_num,
              CASE state
                   WHEN 'Open'
                   THEN ROW_NUMBER() OVER (PARTITION BY id ORDER BY state_time)
              END AS open_row_num,
              CASE state
                   WHEN 'Fixed'
                   THEN ROW_NUMBER() OVER (PARTITION BY id ORDER BY state_time)
              END AS fixed_row_num
       FROM ...
     )

Это дает мне данные, подобные следующим:

id  state   state_time           new_row_group  open_row_group  fixed_row_group
1   New     2009-03-03 00:03:31  1
1   Closed  2009-03-04 04:15:27  1
2   New     2010-05-22 14:38:49  1
2   Open    2010-05-22 14:39:14  1              2
2   Fixed   2010-05-22 17:15:27  1              2               3

Я хотел бы данные, подобные следующим:

id  new_in               new_out              open_in              open_out             fixed_in             fixed_out
1   2009-03-03 00:03:31  2009-03-04 04:15:27
2   2010-05-22 14:38:49  2010-05-22 14:39:14  2010-05-22 14:39:14  2010-05-22 17:15:27  2010-05-22 17:15:27

Как можноЯ поворачиваю данные, чтобы получить эти пары дат для каждого id?

Редактировать: , чтобы уточнить, id может входить и выходить из состояния несколько раз.Например, id может перейти от Нового к Открытому к Фиксированному к Открытому к Фиксированному к Закрытому.В этом случае должно быть столько строк, сколько необходимо для хранения всех времен состояния, например:

id  new_in               new_out              open_in              open_out             fixed_in             fixed_out
4   2009-01-01 00:00:00  2009-01-02 00:00:00  2009-01-02 00:00:00  2009-01-03 00:00:00  2009-01-03 00:00:00  2009-01-04 00:00:00
4                                             2009-01-04 00:00:00  2009-01-05 00:00:00  2009-01-05 00:00:00  2009-01-06 00:00:00

Ответы [ 3 ]

2 голосов
/ 24 июня 2010

Сара

Вот пример с вашими примерами данных:

SQL> create table yourtable (id,state,state_time)
  2  as
  3  select 1, 'New', to_date('2009-03-03 00:03:31','yyyy-mm-dd hh24:mi:ss') from dual union all
  4  select 1, 'Closed', to_date('2009-03-04 04:15:27','yyyy-mm-dd hh24:mi:ss') from dual union all
  5  select 2, 'New', to_date('2010-05-22 14:38:49','yyyy-mm-dd hh24:mi:ss') from dual union all
  6  select 2, 'Open', to_date('2010-05-22 14:39:14','yyyy-mm-dd hh24:mi:ss') from dual union all
  7  select 2, 'Fixed', to_date('2010-05-22 17:15:27','yyyy-mm-dd hh24:mi:ss') from dual union all
  8  select 3, 'New', date '2009-01-01' from dual union all
  9  select 3, 'Open', date '2009-01-02' from dual union all
 10  select 3, 'Fixed', date '2009-01-03' from dual union all
 11  select 3, 'Open', date '2009-01-04' from dual union all
 12  select 3, 'Fixed', date '2009-01-05' from dual union all
 13  select 3, 'Closed', date '2009-01-06' from dual
 14  /

Table created.

Запрос:

SQL> select id
  2       , max(decode(state,'New',state_time))   new_in
  3       , max(decode(state,'New',out_time))     new_out
  4       , max(decode(state,'Open',state_time))  open_in
  5       , max(decode(state,'Open',out_time))    open_out
  6       , max(decode(state,'Fixed',state_time)) fixed_in
  7       , max(decode(state,'Fixed',out_time))   fixed_out
  8    from ( select id
  9                , state
 10                , state_time
 11                , max(cnt) over (partition by id order by state_time) the_row
 12                , lead(state_time) over (partition by id order by state_time) out_time
 13             from ( select id
 14                         , state
 15                         , state_time
 16                         , count(*) over (partition by id,state order by state_time) cnt
 17                      from yourtable
 18                  )
 19         )
 20   group by id
 21       , the_row
 22   order by id
 23       , the_row
 24  /

        ID NEW_IN              NEW_OUT             OPEN_IN             OPEN_OUT            FIXED_IN            FIXED_OUT
---------- ------------------- ------------------- ------------------- ------------------- ------------------- -------------------
         1 03-03-2009 00:03:31 04-03-2009 04:15:27
         2 22-05-2010 14:38:49 22-05-2010 14:39:14 22-05-2010 14:39:14 22-05-2010 17:15:27 22-05-2010 17:15:27
         3 01-01-2009 00:00:00 02-01-2009 00:00:00 02-01-2009 00:00:00 03-01-2009 00:00:00 03-01-2009 00:00:00 04-01-2009 00:00:00
         3                                         04-01-2009 00:00:00 05-01-2009 00:00:00 05-01-2009 00:00:00 06-01-2009 00:00:00

4 rows selected.

Чтобы понять, как это работает, выполните запрос изнутри и проверьте промежуточные наборы результатов. Пожалуйста, дайте мне знать, если вам нужно дополнительное объяснение.

С уважением, Роб.

0 голосов
/ 24 июня 2010

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

select id, 
       min(case state when 'New' then state_time else null end) as new_in,
       max(case state when 'New' then out_state_time else null end) as new_out,
       min(case state when 'Open' then state_time else null end) as open_in,
       max(case state when 'Open' then out_state_time else null end) as open_out,
       min(case state when 'Fixed' then state_time else null end) as fixed_in,
       max(case state when 'Fixed' then out_state_time else null end) as fixed_out
from
    (select id, 
            state, 
            state_time, 
            lead(state_time) over (partition by id 
                                   order by state_time) as out_state_time
     from ...
    )
group by id

Аналитическая функция lead получает следующую строку, описаннуюоператор разбиения / порядка, так что это самый простой способ узнать, когда состояние изменилось.Средний запрос - это основной сводный запрос (преобразование столбцов в строки).

0 голосов
/ 24 июня 2010
select news.id, news.state_time as new_in, min(not_news.state_time) as new_out
   , min(opens.state_time) as open_in
   , min(not_opens.state_time) as open_out
   , min(closes.state_time) as close_in
   , min(not_closed.state_time) as close_out
from
   (SELECT id,
          state,
          state_time
      from mytable
      where state = 'New' ) news
   left join
   (SELECT id,
          state,
          state_time
      from mytable
      where state <> 'New' ) not_news     on news.id = not_news.id and news.state_time <= not_news.state_time
   left join
   (SELECT id,
          state,
          state_time
      from mytable
      where state = 'Open' ) opens     on news.id = opens.id and news.state_time <= opens.state_time
   left join
   (SELECT id,
          state,
          state_time
      from mytable
      where state not in ('New', 'Open' )) not_opens     on news.id = opens.id and news.state_time <= opens.state_time    and opens.state_time <= not_opens.state_time
   left join
   (SELECT id,
          state,
          state_time
      from mytable
      where state = 'Closed' ) closes     on news.id = closes.id and news.state_time <= closes.state_time
   left join
   (SELECT id,
          state,
          state_time
      from mytable
      where state not in ('Closed' )) not_closed     on news.id = not_closed.id and news.state_time <= closes.state_time    and closes.state_time <= not_closed.state_time
group by news.id, news.state_time
order by id,  news.state_time

Мои данные испытаний (позаимствованы у Роба):

    create table mytable (id,state,state_time) 
    as 
    select 1, 'New', to_date('2009-03-03 00:03:31','yyyy-mm-dd hh24:mi:ss') from dual union all 
    select 1, 'Closed', to_date('2009-03-04 04:15:27','yyyy-mm-dd hh24:mi:ss') from dual union all 
    select 2, 'New', to_date('2010-05-22 14:38:49','yyyy-mm-dd hh24:mi:ss') from dual union all 
    select 2, 'Open', to_date('2010-05-22 14:39:14','yyyy-mm-dd hh24:mi:ss') from dual union all 
    select 2, 'Fixed', to_date('2010-05-22 17:15:27','yyyy-mm-dd hh24:mi:ss') from dual union all 
    select 3, 'New', date '2009-01-01' from dual union all 
    select 3, 'Open', date '2009-01-02' from dual union all 
   select 3, 'Fixed', date '2009-01-03' from dual union all 
   select 3, 'Open', date '2009-01-04' from dual union all 
   select 3, 'Fixed', date '2009-01-05' from dual union all 
   select 3, 'Closed', date '2009-01-06' from dual 

Результаты запроса:

ID   NEW_IN            NEW_OUT           OPEN_IN           OPEN_OUT          CLOSE_IN CLOSE_OUT
1    3/3/2009 12:03:31 3/4/2009 4:15:27  3/4/2009 4:15:27   
2    5/22/2010 2:38:49 5/22/2010 2:39:14 5/22/2010 2:39:14 5/22/2010 5:15:27        
3    1/1/2009          1/2/2009          1/2/2009          1/3/2009          1/6/2009   

Надеюсь, вы можете прочитать выше,У меня проблемы с форматированием.

...