1

次のように定義されたOracle 10のテーブルがあります。

LOCATION   HOUR              STATUS
--------------------------------------
10         12/10/09 5:00PM      1
10         12/10/09 6:00PM      1
10         12/10/09 7:00PM      2
10         12/10/09 8:00PM      1
10         12/10/09 9:00PM      3
10         12/10/09 10:00PM     3
10         12/10/09 11:00PM     3

この表は、さまざまな場所と少数のステータス値について続きます。各行は、1 つの場所の 1 時間をカバーしています。データは、その 1 時間の間に特定の場所から収集され、チャンクで処理されます。データが利用できる場合とそうでない場合があり、その情報はステータスにエンコードされます。上記の表を次のように変換できるように、特定のステータスの実行を見つけようとしています。

LOCATION   STATUS     START               END
-----------------------------------------------------------
10         1          12/10/09 5:00PM     12/10/09 7:00PM
10         2          12/10/09 7:00PM     12/10/09 8:00PM  
10         1          12/10/09 8:00PM     12/10/09 9:00PM
10         3          12/10/09 9:00PM     12/11/09 12:00AM 

基本的に、テーブルを特定のステータスの各ストレッチを定義する行に凝縮します。リード/ラグを使用して開始点と終了点を把握するなど、さまざまなトリックを試しましたが、すべて失敗に終わりました。これまでのところ機能する唯一のトリックは、プログラムで値を 1 つずつ処理することですが、これは時間がかかります。Oracleで直接行うためのアイデアはありますか? ありがとう!

4

3 に答える 3

2

ANSI SQL ソリューションは次のとおりです。

select      t1.location
,           t1.status
,           min(t1.hour)                                      AS "start" -- first of stretch of same status
,           coalesce(t2.hour, max(t1.hour) + INTERVAL '1' HOUR) AS "end"
from        t_intervals t1             -- base table, this is what we are condensing
left join   t_intervals t2             -- finding the first datetime after a stretch of t1
on          t1.location = t2.location  -- demand same location
and         t1.hour     < t2.hour      -- demand t1 before t2
and         t1.status  != t2.status    -- demand different status 
left join   t_intervals t3             -- finding rows not like t1, with hour between t1 and t2
on          t1.location = t3.location
and         t1.status  != t3.status
and         t1.hour     < t3.hour
and         t2.hour     > t3.hour
where       t3.status is null          -- demand that t3 does not exist, in other words, t2 marks a status transition
group by    t1.location                -- condense on location, status.
,           t1.status
,           t2.hour                    -- this pins the status transition
order by    t1.location
,           t1.status
,           min(t1.hour)
于 2009-12-28T22:25:45.620 に答える
1

OK、Oracleの構文がわからないことをお詫びしますが、以下のSybaseの構文が十分に明確であることを願っています(読みやすくするために2つの一時テーブルを作成する3つのクエリに分割しましたが、サブクエリとして再結合できます。 Oracleで1時間加算/減算する方法を知ってdateadd(hh...)いる、Sybaseでそれを行う

SELECT * FROM T
INTO   #START_OF_PERIODS
WHERE NOT EXISTS (
    SELECT 1 FROM T_BEFORE
    WHERE T.LOCATION = T_BEFORE.LOCATION
      AND T.STATUS   = T_BEFORE.STATUS
      AND T.HOUR     = dateadd(hh, T_BEFORE.HOUR, 1)
   )

SELECT * FROM T
INTO   #END_OF_PERIODS
WHERE NOT EXISTS (
    SELECT 1 FROM T_AFTER
    WHERE T.LOCATION = T_AFTER.LOCATION
      AND T.STATUS   = T_AFTER.STATUS
      AND T.HOUR     = dateadd(hh, T_AFTER.HOUR, -1)
   )

SELECT T1.LOCATION, T1.STATUS, T1.HOUR AS 'START', MIN(T2.HOUR) AS 'END'
FROM   #START_OF_PERIODS 'T1', #END_OF_PERIODS 'T2'
WHERE  T1.LOCATION = T2.LOCATION
  AND  T1.STATUS   = T2.STATUS
  AND  T1.HOUR    <= T2.HOUR
GROUP BY T1.LOCATION, T1.STATUS, T1.HOUR 
    -- May need to add T2.LOCATION, T2.STATUS to GROUP BY???
于 2009-12-28T22:07:05.767 に答える
0

ストアド プロシージャについて考えたことはありますか? それが最も読みやすい解決策になると思います。

基本的な考え方:

  1. 1 つの建物に対して正しい順序で行を提供する select ステートメントを実行します。
  2. 結果を行ごとに繰り返し、ステータスが変化するたびに新しい「実行」レコードを書き込み、結果セットの最後に到達します。

それが最速の方法でもあるかどうかをテストする必要があります。レコードの数によっては、これはまったく問題にならない場合があります。

于 2009-12-28T22:18:23.180 に答える