2017-08-25 8 views
0

私はかなり複雑なSQLクエリを作成しなければならないという立場にあります。PostgreSQLクエリを使用して日々の統計を使って時系列を生成する

私はordersというテーブルと、時間の経過とともにこれらの注文の状態を記録する関連テーブルorder_state_history(下記参照)を持っています。

これで、その日の終わりに特定の州にあった注文数を含む一連の行(1日1行)を生成する必要があります(report参照)。また、私はorder.type = 1の注文だけを考えたいと思う。

データはPostgreSQLデータベースに存在します。私はすでにGENERATE_SERIES(DATE '2001-01-01', CURRENT_DATE, '1 DAY'::INTERVAL) daysを使って時系列を生成する方法を見つけました。これは、状態の変化が記録されていない日の行を生成することができます。

私の現在のアプローチは、すべて一緒にordersorder_state_historydaysの生成シリーズに参加し、DATE(order_state_history.timestamp) > DATE(days)を持って、その後、何とかfirst_value(order_state_history.new_state) OVER (PARTITION_BY(orders.id) ORDER BY order_state_history.timestamp DESC)によってその日に各注文の最終状態を取得するすべての行をフィルタリングしようとすることですが、この私の小さなSQLの経験が私を放棄する場所です。

私はこの問題を回避することはできません。

これは単一のクエリでも解決できますか、1日に1つのクエリを実行する何らかのインテリジェントスクリプトによってデータを計算することをお勧めしますか? 問題に対する合理的なアプローチは何でしょうか?

orders===    
id  type   
10000 1   
10001 1   
10002 2   
10003 2   
10004 1   


order_state_history===    
order_id index timestamp   new_state 
10000  1  01.01.2001 12:00 NEW 
10000  2  02.01.2001 13:00 ACTIVE 
10000  3  03.01.2001 14:00 DONE 
10001  1  02.01.2001 13:00 NEW 
10002  1  03.01.2001 14:00 NEW 
10002  2  05.01.2001 10:00 ACTIVE 
10002  3  05.01.2001 14:00 DONE 
10003  1  07.01.2001 04:00 NEW 
10004  1  05.01.2001 14:00 NEW 
10004  2  10.01.2001 17:30 DONE 


Expected result===    
date   new_orders active_orders done_orders 
01.01.2001 1    0    0 
02.01.2001 1    1    0 
03.01.2001 1    0    1 
04.01.2001 1    0    1 
05.01.2001 2    0    1 
06.01.2001 2    0    1 
07.01.2001 2    0    1 
08.01.2001 2    0    1 
09.01.2001 2    0    1 
10.01.2001 1    0    2 
+0

期待される結果を確認してください(03.01に2つの新規注文があるのはなぜですか?)、次に予想される行を少なくとも05.01まで追加してください。 – klin

+0

関連するすべての行を追加しました。 03.01に両方の02.01のために2つの新しい注文があります。 03.01。新しい注文が作成されました(10001および10002)。注文10001 **は州NEWにとどまりますので、それ以降のすべての日にカウントされます。カウントは合計であり、結果行 'new_orders'はステータスが変更されたかどうかにかかわらず、1日の終わりにNEW状態にあるすべての注文をカウントします。 –

+0

しかし、10002はタイプ2なので、カウントしないでください。 – klin

答えて

0

ステップ1.計算ACTIVE値NEW = 1を使用して、各注文の状態の累積和、= 1、DONE = 2:

select 
    order_id, timestamp::date as day, 
    sum(case new_state when 'DONE' then 2 else 1 end) over w as state 
from order_state_history h 
join orders o on o.id = h.order_id 
where o.type = 1 
window w as (partition by order_id order by timestamp) 

order_id | day  | state 
----------+------------+------- 
    10000 | 2001-01-01 |  1 
    10000 | 2001-01-02 |  2 
    10000 | 2001-01-03 |  4 
    10001 | 2001-01-02 |  1 
    10004 | 2001-01-05 |  1 
    10004 | 2001-01-10 |  3 
(6 rows) 

ステップ2を計算するための各遷移行列

select 
    order_id, day, state, 
    case when state = 1 then 1 when state = 2 or state = 3 then -1 else 0 end as new, 
    case when state = 2 then 1 when state = 4 then -1 else 0 end as active, 
    case when state > 2 then 1 else 0 end as done 
from (
    select 
     order_id, timestamp::date as day, 
     sum(case new_state when 'DONE' then 2 else 1 end) over w as state 
    from order_state_history h 
    join orders o on o.id = h.order_id 
    where o.type = 1 
    window w as (partition by order_id order by timestamp) 
    ) s 

order_id | day  | state | new | active | done 
----------+------------+-------+-----+--------+------ 
    10000 | 2001-01-01 |  1 | 1 |  0 | 0 
    10000 | 2001-01-02 |  2 | -1 |  1 | 0 
    10000 | 2001-01-03 |  4 | 0 |  -1 | 1 
    10001 | 2001-01-02 |  1 | 1 |  0 | 0 
    10004 | 2001-01-05 |  1 | 1 |  0 | 0 
    10004 | 2001-01-10 |  3 | -1 |  0 | 1 
(6 rows) 

ステップ3の計算シリーズの各状態の累積和:ステップ1からの状態に基づいて順序(2 NEW->がACTIVE、3は、DONE 4つの手段をアクティブ - > DONE> NEW-手段)日:

select distinct 
    day::date, 
    sum(new) over w as new, 
    sum(active) over w as active, 
    sum(done) over w as done 
from generate_series('2001-01-01'::date, '2001-01-10', '1d'::interval) day 
left join (
    select 
     order_id, day, state, 
     case when state = 1 then 1 when state = 2 or state = 3 then -1 else 0 end as new, 
     case when state = 2 then 1 when state = 4 then -1 else 0 end as active, 
     case when state > 2 then 1 else 0 end as done 
    from (
     select 
      order_id, timestamp::date as day, 
      sum(case new_state when 'DONE' then 2 else 1 end) over w as state 
     from order_state_history h 
     join orders o on o.id = h.order_id 
     where o.type = 1 
     window w as (partition by order_id order by timestamp) 
     ) s 
    ) s 
using(day) 
window w as (order by day) 
order by 1 

    day  | new | active | done 
------------+-----+--------+------ 
2001-01-01 | 1 |  0 | 0 
2001-01-02 | 1 |  1 | 0 
2001-01-03 | 1 |  0 | 1 
2001-01-04 | 1 |  0 | 1 
2001-01-05 | 2 |  0 | 1 
2001-01-06 | 2 |  0 | 1 
2001-01-07 | 2 |  0 | 1 
2001-01-08 | 2 |  0 | 1 
2001-01-09 | 2 |  0 | 1 
2001-01-10 | 1 |  0 | 2 
(10 rows) 
関連する問題