2016-11-20 3 views
3

タスクを表す各行に開始時刻と終了時刻が指定されていると、各タスクの開始時に実行中のタスク(開始済みと終了していない)の数を計算する方法COUNT OVERのウィンドウ関数を使用して(それ自体を含む)?窓の機能は正しいアプローチですか?現在の行にCOUNT()がオーバーしている

例、与えられたテーブルtasks

task_id start_time end_time 
    a   1   10 
    b   2   5 
    c   5   15 
    d   8   13 
    e  12   20 
    f  21   30 

計算running_tasks

task_id start_time end_time running_tasks 
    a   1   10   1   # a 
    b   2   5   2   # a,b 
    c   5   15   2   # a,c (b has ended) 
    d   8   13   3   # a,c,d 
    e  12   20   3   # c,d,e (a has ended) 
    f  21   30   1   # f (c,d,e have ended) 

答えて

2
select  task_id,start_time,end_time,running_tasks 

from  (select  task_id,tm,op,start_time,end_time 

         ,sum(op) over 
         (
          order by tm,op 
          rows  unbounded preceding 
         ) as running_tasks 

      from  (select  task_id,start_time as tm,1 as op,start_time,end_time 
         from  tasks 

         union all 

         select  task_id,end_time as tm,-1 as op,start_time,end_time 
         from  tasks 
         ) t 
      )t 

where  op = 1 
; 
+0

Dudu、ありがとうスマートソリューションを参照してください。ウィンドウ関数を現在の行で調整できる、より一般化されたソリューションを得ることを望んでいました - これは可能ですか? –

+0

@NewDev、私はあなたの意図を得ないので、例を挙げてください。 –

+0

現在の行に対して条件を満たす行のみをカウントするために、条件付きのウィンドウ関数をCOUNTすることは可能ですか?たとえば、現在の行の 'start_time = 8'の場合、window関数は' end_time> 8 'と 'start_time <= 8'の行だけをカウントできますか? –

2

でき、この場合には自己結合である相関サブクエリを使用することができます。分析機能は必要ありません。 「それは新しいユーザーに分析関数を説明するために、一般的にはより困難だ」 - エリオットが述べたように

WITH tasks AS (
    SELECT 
    task_id, 
    start_time, 
    end_time 
    FROM UNNEST(ARRAY<STRUCT<task_id STRING, start_time INT64, end_time INT64>>[ 
    ('a', 1, 10), 
    ('b', 2, 5), 
    ('c', 5, 15), 
    ('d', 8, 13), 
    ('e', 12, 20), 
    ('f', 21, 30) 
    ]) 
) 
SELECT 
    *, 
    (SELECT COUNT(*) FROM tasks t2 
    WHERE t.start_time >= t2.start_time AND 
    t.start_time < t2.end_time) AS running_tasks 
FROM tasks t 
ORDER BY task_id; 
+0

"解析機能は必要ありません"?そして、これはあなたにとって利点と思われますか? –

+1

はい - ジョインや集計などの概念よりも、新しいSQLユーザーに分析関数を説明するのは一般的に難しいです。この場合、OPに問題の2つの異なる視点を与える回答がありました。これは大変です:) –

+0

これは表示されましたが、少なくとも「覚えておくよ! –

2

:(UIで「表示オプション」の下に「使用レガシーSQLを」オフ)standard SQLを有効にした後、あなたは、この例を実行することができます確立されたユーザーでさえ常に100%優れているとは限りません(非常に近い)。
Dudu Markovitzの回答は素晴らしいですが、残念ながら、(少なくとも私が質問をどのように理解したかによると)それはまだ間違っています。私が思う

task_id start_time end_time 
    a   1   10 
    aa  1   2 
    aaa  1   8 
    b   2   5 
    c   5   15 
    d   8   13 
    e  12   20 
    f  21   30 

: - 一例として、結果

「タスクを実行している」ので、これらのタスクは間違って持っている - あなたは複数のタスクが同じのstart_timeで開始しているとき、それは正しくない場合がある例の下に考えますあなたの代わりに下記の

task_id start_time end_time running_tasks 
    a   1   10   1   
    aa  1   2   2   
    aaa  1   8   3   
    b   2   5   3   
    c   5   15   3   
    d   8   13   3   
    e  12   20   3   
    f  21   30   1   
を取得します - あなたはドゥドゥのコードでそれをしようとする場合

task_id start_time end_time running_tasks 
    a   1   10   3   # a,aa,aaa 
    aa  1   2   3   # a,aa,aaa 
    aaa  1   8   3   # a,aa,aaa 
    b   2   5   3   # a,aaa,b (aa has ended) 
    c   5   15   3   # a,aaa,c (b has ended) 
    d   8   13   3   # a,c,d (aaa has ended) 
    e  12   20   3   # c,d,e (a has ended) 
    f  21   30   1   # f (c,d,e have ended)  

:、あなたは結果の下に期待します3210

あなたはタスクaとaaの結果が間違っています。
理由は、RANGE UNBOUNDED PRECEDINGの代わりにROWS UNBOUNDED PRECEDINGを使用しているためです - 小さくても非常に重要なニュアンスです!

問合せは、あなたに正しい結果が得られますので、下に

SELECT task_id,start_time,end_time,running_tasks 
FROM (
    SELECT 
    task_id, tm, op, start_time, end_time, 
    SUM(op) OVER (ORDER BY tm ,op RANGE UNBOUNDED PRECEDING) AS running_tasks 
    FROM (
    SELECT 
     task_id, start_time AS tm, 1 AS op, start_time, end_time 
    FROM tasks UNION ALL 
    SELECT 
     task_id, end_time AS tm, -1 AS op, start_time, end_time 
    FROM tasks 
) t 
)t 
WHERE op = 1 
ORDER BY start_time  

簡単な要約:
ROWS UNBOUNDED PRECEDING - 窓枠を設定します - 行位置に基づいて、窓枠が
RANGEはUNBOUNDED PRECEDINGは
に対し設定行の値に基づいて

再び - エリオットが言及したように、これはJOINの概念よりもはるかに複雑です。しかし、それは価値があります。 Window Frame ClauseとROWS vs RANGEの詳細については、

関連する問題