запрос на разделение разделов при физическом разделении - PullRequest
0 голосов
/ 30 мая 2019

У меня есть таблица, которая содержит историю заказов / отгрузок. Базовая фиктивная версия:

ORDERS
order_no | order_stat | stat_date 
 2       | Planned    |  01-Jan-2000
 2       | Picked     |  15-Jan-2000
 2       | Planned    |  17-Jan-2000
 2       | Planned    |  05-Feb-2000
 2       | Planned    |  31-Mar-2000
 2       | Picked     |  05-Apr-2000
 2       | Shipped    |  10-Apr-2000

Мне нужно выяснить, как долго каждый заказ находился в статусе / фазе каждого заказа. Единственная проблема заключается в том, что когда я создаю раздел для order_no и order_stat, я получаю результаты, которые имеют смысл, но не являются тем, что я ищу.

Мой sql:

 select
    order_no
    ,order_stat
    ,stat_date
    ,lag(stat_date, 1) over (partition by order_no order by stat_date) prev_stat_date
    ,stat_date - lag(stat_date, 1) over (partition by order_no order by stat_date) date_diff
    ,row_number() over(partition by order_no, order_stat order by stat_date) rnk
 from
    orders

даст мне следующие результаты:

order_no | order_stat | stat_date     | prev_stat_date  |    rnk     
 2       | Planned    |  01-Jan-2000  |                 |  1
 2       | Picked     |  15-Jan-2000  |  01-Jan-2000    |  1
 2       | Planned    |  17-Jan-2000  |  15-Jan-2000    |  2
 2       | Planned    |  05-Feb-2000  |  17-Jan-2000    |  3
 2       | Planned    |  31-Mar-2000  |  05-Feb-2000    |  4
 2       | Picked     |  05-Apr-2000  |  31-Mar-2000    |  2
 2       | Shipped    |  10-Apr-2000  |  05-Apr-2000    |  1  

Я бы хотел, чтобы результаты выглядели так (рнк начинает заново, когда он возвращается к предыдущей статистике):

order_no | order_stat | stat_date     | prev_stat_date  |    rnk     
 2       | Planned    |  01-Jan-2000  |                 |  1
 2       | Picked     |  15-Jan-2000  |  01-Jan-2000    |  1
 2       | Planned    |  17-Jan-2000  |  15-Jan-2000    |  1
 2       | Planned    |  05-Feb-2000  |  17-Jan-2000    |  2
 2       | Planned    |  31-Mar-2000  |  05-Feb-2000    |  3
 2       | Picked     |  05-Apr-2000  |  31-Mar-2000    |  1
 2       | Shipped    |  10-Apr-2000  |  05-Apr-2000    |  1

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

Ответы [ 2 ]

1 голос
/ 30 мая 2019

Если я правильно понимаю, это проблема пробелов и островков.

Разницу номеров строк можно использовать для идентификации "островков", а затем для перечисления значений:

select t.*,
       row_number() over (partition by order_no, order_stat, seqnum - seqnum_2 order by stat_date) as your_rank
from (select o.*,
             row_number() over (partition by order_no order by stat_date) as seqnum,
             row_number() over (partition by order_no, order_stat order by stat_date) as seqnum_2
      from orders o
     ) t;

Я пропустил другие столбцы (например, lag()), чтобы вы могли видеть логику. Может быть немного трудно понять, почему это работает. Если вы посмотрите на некоторые строки из подзапроса, вы, вероятно, увидите, как разность номеров строк определяет нужные вам группы.

0 голосов
/ 30 мая 2019

Продолжая @ Гордона Табибитозан , когда у вас есть группировки, вы можете получить как заказ в каждой группе, так и истекшее количество дней для каждого члена группы:

-- CTE for sample data
with orders (order_no, order_stat, stat_date) as (
            select 2, 'Planned', date '2000-01-01' from dual
  union all select 2, 'Picked',  date '2000-01-15' from dual
  union all select 2, 'Planned', date '2000-01-17' from dual
  union all select 2, 'Planned', date '2000-02-05' from dual
  union all select 2, 'Planned', date '2000-03-31' from dual
  union all select 2, 'Picked ', date '2000-04-05' from dual
  union all select 2, 'Shipped', date '2000-04-10' from dual
)
-- actual query
select order_no, order_stat, stat_date, grp,
  dense_rank() over (partition by order_no, order_stat, grp order by stat_date) as rnk,
  stat_date - min(stat_date) keep (dense_rank first order by stat_date)
                over (partition by order_no, order_stat, grp) as stat_days
from (
  select order_no, order_stat, stat_date,
    row_number() over (partition by order_no order by stat_date)
      - row_number() over (partition by order_no, order_stat order by stat_date) as grp
  from orders
)
order by order_no, stat_date;

  ORDER_NO ORDER_S STAT_DATE         GRP        RNK  STAT_DAYS
---------- ------- ---------- ---------- ---------- ----------
         2 Planned 2000-01-01          0          1          0
         2 Picked  2000-01-15          1          1          0
         2 Planned 2000-01-17          1          1          0
         2 Planned 2000-02-05          1          2         19
         2 Planned 2000-03-31          1          3         74
         2 Picked  2000-04-05          5          1          0
         2 Shipped 2000-04-10          6          1          0

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

Не совсем точно, что вы хотите, но вы можете расширить еще, например:

with cte1 (order_no, order_stat, stat_date, grp) as (
  select order_no, order_stat, stat_date,
    row_number() over (partition by order_no order by stat_date)
      - row_number() over (partition by order_no, order_stat order by stat_date)
  from orders
),
cte2 (order_no, order_stat, stat_date, grp, grp_date, rnk) as (
  select order_no, order_stat, stat_date, grp,
    min(stat_date) keep (dense_rank first order by stat_date)
      over (partition by order_no, order_stat, grp),
    dense_rank() over (partition by order_no, order_stat, grp order by stat_date)
  from cte1
)
select order_no, order_stat, stat_date, grp, grp_date, rnk,
  stat_date - grp_date as stat_days_so_far,
  case
    when order_stat != 'Shipped' then
      coalesce(first_value(stat_date)
                 over (partition by order_no order by grp_date
                   range between 1 following and unbounded following), trunc(sysdate))
        - min(stat_date) keep (dense_rank first order by stat_date)
            over (partition by order_no, order_stat, grp)
  end as stat_days_total,
  stat_date - min(stat_date) over (partition by order_no) as order_days_so_far,
  case
    when max(order_stat) keep (dense_rank last order by stat_date)
           over (partition by order_no) = 'Shipped' then
      max(stat_date) over (partition by order_no)
    else
      trunc(sysdate)
  end
    - min(stat_date) over (partition by order_no) as order_days_total
from cte2
order by order_no, stat_date;

который для данных вашего образца дает:

  ORDER_NO ORDER_S STAT_DATE         GRP GRP_DATE          RNK STAT_DAYS_SO_FAR STAT_DAYS_TOTAL ORDER_DAYS_SO_FAR ORDER_DAYS_TOTAL
---------- ------- ---------- ---------- ---------- ---------- ---------------- --------------- ----------------- ----------------
         2 Planned 2000-01-01          0 2000-01-01          1                0              14                 0              100
         2 Picked  2000-01-15          1 2000-01-15          1                0               2                14              100
         2 Planned 2000-01-17          1 2000-01-17          1                0              79                16              100
         2 Planned 2000-02-05          1 2000-01-17          2               19              79                35              100
         2 Planned 2000-03-31          1 2000-01-17          3               74              79                90              100
         2 Picked  2000-04-05          5 2000-04-05          1                0               5                95              100
         2 Shipped 2000-04-10          6 2000-04-10          1                0                               100              100

Я включил некоторую логику, чтобы предположить, что «Отправлено» является окончательным статусом, и если это не было достигнуто, то последний статус все еще работает - так что посчитайте до сегодняшнего дня. Это может быть неправильно, и у вас могут быть другие значения конечного состояния (например, отменены). Во всяком случае, несколько вещей для вас, чтобы исследовать и играть с ...

Возможно, вы сможете сделать нечто подобное с match_recognize, но я оставлю это кому-то еще.

...