2

この質問を念頭に置いていたので、このサイトを発見したので、ここに投稿することにしました。

特定の「オブジェクト」(OOP オブジェクトではなく一般的な意味) のタイムスタンプと状態を含むテーブルがあるとします。単一の SQL ステートメント (内部の SELECT と UNION はカウントされません) を使用して、状態と別の (または同じ) 状態の次の発生 (「トリップ」と呼びます) の間の時間を計算する最適な方法はありますか?

例: 以下の場合、初期から完了までの移動時間は 6 日ですが、初期からレビューまでは 2 日です。

2008-08-01 13:30:00 - 初期
2008-08-02 13:30:00 - 作業
2008-08-03 13:30:00 - レビュー
2008-08-04 13:30:00 - 作業
2008- 08-05 13:30:00 - レビュー
2008-08-06 13:30:00 - 承認
済み 2008-08-07 13:30:00 - 完了

一般的である必要はありません。一般的でない場合は、ソリューションが固有のSGBDを説明してください。

4

10 に答える 10

1

PostgreSQL構文:

DROP TABLE ObjectState;
CREATE TABLE ObjectState (
    object_id integer not null,--foreign key
    event_time timestamp NOT NULL,
    state varchar(10) NOT NULL,
    --Other fields 
    CONSTRAINT pk_ObjectState PRIMARY KEY (object_id,event_time)
);

与えられた状態について、与えられたタイプの最初の次の状態を見つけます

select parent.object_id,parent.event_time,parent.state,min(child.event_time) as ch_event_time,min(child.event_time)-parent.event_time as step_time
from 
    ObjectState parent
    join ObjectState child on (parent.object_id=child.object_id and parent.event_time<child.event_time)
where 
    --Starting state 
    parent.object_id=1 and parent.event_time=to_timestamp('01-Aug-2008 13:30:00','dd-Mon-yyyy hh24:mi:ss')
    --needed state
    and child.state='Review'
group by parent.object_id,parent.event_time,parent.state;

このクエリは可能な限り最短ではありませんが、理解しやすく、他のクエリの一部として使用できる必要があります。

特定のオブジェクトのイベントとその期間を一覧表示します

select parent.object_id,parent.event_time,parent.state,min(child.event_time) as ch_event_time,
       CASE WHEN parent.state<>'Done' and min(child.event_time) is null THEN (select localtimestamp)-parent.event_time ELSE min(child.event_time)-parent.event_time END  as step_time
from 
    ObjectState parent
    left outer join ObjectState child on (parent.object_id=child.object_id and parent.event_time<child.event_time)
where parent.object_id=4    
group by parent.object_id,parent.event_time,parent.state
order by parent.object_id,parent.event_time,parent.state;

「完了」していないオブジェクトの現在の状態を一覧表示します

select states.object_id,states.event_time,states.state,(select localtimestamp)-states.event_time as step_time
from
    (select parent.object_id,parent.event_time,parent.state,min(child.event_time) as ch_event_time,min(child.event_time)-parent.event_time as step_time
     from 
        ObjectState parent
        left outer join ObjectState child on (parent.object_id=child.object_id and parent.event_time<child.event_time)       
     group by parent.object_id,parent.event_time,parent.state) states
where     
    states.object_id not in (select object_id from ObjectState where state='Done')
    and ch_event_time is null;

テストデータ

insert into ObjectState (object_id,event_time,state)
select 1,to_timestamp('01-Aug-2008 13:30:00','dd-Mon-yyyy hh24:mi:ss'),'Initial' union    all
select 1,to_timestamp('02-Aug-2008 13:40:00','dd-Mon-yyyy hh24:mi:ss'),'Work' union all
select 1,to_timestamp('03-Aug-2008 13:50:00','dd-Mon-yyyy hh24:mi:ss'),'Review' union all
select 1,to_timestamp('04-Aug-2008 14:30:00','dd-Mon-yyyy hh24:mi:ss'),'Work' union all
select 1,to_timestamp('04-Aug-2008 16:20:00','dd-Mon-yyyy hh24:mi:ss'),'Review' union all
select 1,to_timestamp('06-Aug-2008 18:00:00','dd-Mon-yyyy hh24:mi:ss'),'Accepted' union all
select 1,to_timestamp('07-Aug-2008 21:30:00','dd-Mon-yyyy hh24:mi:ss'),'Done';


insert into ObjectState (object_id,event_time,state)
select 2,to_timestamp('01-Aug-2008 13:30:00','dd-Mon-yyyy hh24:mi:ss'),'Initial' union all
select 2,to_timestamp('02-Aug-2008 13:40:00','dd-Mon-yyyy hh24:mi:ss'),'Work' union all
select 2,to_timestamp('07-Aug-2008 13:50:00','dd-Mon-yyyy hh24:mi:ss'),'Review' union all
select 2,to_timestamp('14-Aug-2008 14:30:00','dd-Mon-yyyy hh24:mi:ss'),'Work' union all
select 2,to_timestamp('15-Aug-2008 16:20:00','dd-Mon-yyyy hh24:mi:ss'),'Review' union all
select 2,to_timestamp('16-Aug-2008 18:02:00','dd-Mon-yyyy hh24:mi:ss'),'Accepted' union all
select 2,to_timestamp('17-Aug-2008 22:10:00','dd-Mon-yyyy hh24:mi:ss'),'Done';

insert into ObjectState (object_id,event_time,state)
select 3,to_timestamp('12-Sep-2008 13:30:00','dd-Mon-yyyy hh24:mi:ss'),'Initial' union    all
select 3,to_timestamp('13-Sep-2008 13:40:00','dd-Mon-yyyy hh24:mi:ss'),'Work' union all
select 3,to_timestamp('14-Sep-2008 13:50:00','dd-Mon-yyyy hh24:mi:ss'),'Review' union   all
select 3,to_timestamp('15-Sep-2008 14:30:00','dd-Mon-yyyy hh24:mi:ss'),'Work' union all
select 3,to_timestamp('16-Sep-2008 16:20:00','dd-Mon-yyyy hh24:mi:ss'),'Review';


insert into ObjectState (object_id,event_time,state)
select 4,to_timestamp('21-Aug-2008 03:10:00','dd-Mon-yyyy hh24:mi:ss'),'Initial' union all
select 4,to_timestamp('22-Aug-2008 03:40:00','dd-Mon-yyyy hh24:mi:ss'),'Work' union all
select 4,to_timestamp('23-Aug-2008 03:20:00','dd-Mon-yyyy hh24:mi:ss'),'Review' union all
select 4,to_timestamp('24-Aug-2008 04:30:00','dd-Mon-yyyy hh24:mi:ss'),'Work';
于 2008-09-19T15:50:28.033 に答える
1

これは、分析関数を使用した Oracle の方法論です。

with data as (
SELECT 1 trip_id, to_date('20080801 13:30:00','YYYYMMDD HH24:mi:ss') dt, 'Initial'  step from dual UNION ALL
SELECT 1 trip_id, to_date('20080802 13:30:00','YYYYMMDD HH24:mi:ss') dt, 'Work'     step from dual  UNION ALL
SELECT 1 trip_id, to_date('20080803 13:30:00','YYYYMMDD HH24:mi:ss') dt, 'Review'   step from dual  UNION ALL
SELECT 1 trip_id, to_date('20080804 13:30:00','YYYYMMDD HH24:mi:ss') dt, 'Work'     step from dual UNION ALL
SELECT 1 trip_id, to_date('20080805 13:30:00','YYYYMMDD HH24:mi:ss') dt, 'Review'   step from dual  UNION ALL
SELECT 1 trip_id, to_date('20080806 13:30:00','YYYYMMDD HH24:mi:ss') dt, 'Accepted' step from dual  UNION ALL
SELECT 1 trip_id, to_date('20080807 13:30:00','YYYYMMDD HH24:mi:ss') dt, 'Done'     step from dual )
select trip_id,
       step,
       dt - lag(dt) over (partition by trip_id order by dt) trip_time
from  data
/


1   Initial 
1   Work        1
1   Review      1
1   Work        1
1   Review      1
1   Accepted    1
1   Done        1

これらは、伝統的に自己結合を使用する可能性がある状況で非常に一般的に使用されます。

于 2008-09-17T15:01:36.180 に答える
0

私はMySQLでこれをやろうとしました。MySQL にはランク関数がないため、変数を使用する必要があるため、次のようになります。

set @trip1 = 0; set @trip2 = 0;
SELECT trip1.`date` as startdate, datediff(trip2.`date`, trip1.`date`) length_of_trip
FROM
(SELECT @trip1 := @trip1 + 1 as rank1, `date` from trip where state='Initial') as trip1
INNER JOIN
(SELECT @trip2 := @trip2 + 1 as rank2, `date` from trip where state='Done') as trip2
ON rank1 = rank2;

「初期」状態と「完了」状態の間の時間を計算したいと仮定しています。

+---------------------+----------------+
| startdate           | length_of_trip |
+---------------------+----------------+
| 2008-08-01 13:30:00 |              6 |
+---------------------+----------------+
于 2008-09-21T00:10:09.493 に答える
0

シーケンス番号とタイムスタンプがあれば、おそらく簡単です。ほとんどの RDBMS では、自動インクリメント列を作成でき、INSERTステートメントを変更する必要はありません。次に、テーブルをそれ自体のコピーと結合して、デルタを取得します

select after.moment - before.moment, before.state, after.state
from object_states before, object_states after
where after.sequence + 1 = before.sequence

(SQL 構文の詳細は、データベース システムによって異なります)。

于 2008-09-17T13:45:36.597 に答える
0

質問を正確に理解しているかどうかはわかりませんが、次のようにして、1回のパスでテーブルを読み取り、派生テーブルを使用して計算することができます。SQL Server コード:

CREATE TABLE #testing
(
    eventdatetime datetime NOT NULL,
    state varchar(10) NOT NULL
)

INSERT INTO #testing (
    eventdatetime,
    state
) 
SELECT '20080801 13:30:00', 'Initial' UNION ALL
SELECT '20080802 13:30:00', 'Work' UNION ALL
SELECT '20080803 13:30:00', 'Review' UNION ALL
SELECT '20080804 13:30:00', 'Work' UNION ALL
SELECT '20080805 13:30:00', 'Review' UNION ALL
SELECT '20080806 13:30:00', 'Accepted' UNION ALL
SELECT '20080807 13:30:00', 'Done'

SELECT DATEDIFF(dd, Initial, Review)
FROM (
SELECT  MIN(CASE WHEN state='Initial' THEN eventdatetime END) AS Initial,
        MIN(CASE WHEN state='Review' THEN eventdatetime END) AS Review
FROM #testing
) AS A

DROP TABLE #testing
于 2008-09-17T13:33:37.997 に答える
0
create table A (
    At datetime not null,
    State varchar(20) not null
)
go
insert into A(At,State)
select '2008-08-01T13:30:00','Initial' union all
select '2008-08-02T13:30:00','Work' union all
select '2008-08-03T13:30:00','Review' union all
select '2008-08-04T13:30:00','Work' union all
select '2008-08-05T13:30:00','Review' union all
select '2008-08-06T13:30:00','Accepted' union all
select '2008-08-07T13:30:00','Done'
go
--Find trip time from Initial to Done
select DATEDIFF(day,t1.At,t2.At)
from
    A t1
        inner join
    A t2
        on
            t1.State = 'Initial' and
            t2.State = 'Review' and
            t1.At < t2.At
        left join
    A t3
        on
            t3.State = 'Initial' and
            t3.At > t1.At and
            t4.At < t2.At
        left join
    A t4
        on
            t4.State = 'Review' and
            t4.At < t2.At and
            t4.At > t1.At
where
    t3.At is null and
    t4.At is null

参加が許可されているかどうかについては言及していません。t3 と t4 (およびそれらの比較) への結合により、開始状態と終了状態の最初または最後の発生が必要かどうかを指定できます (この場合、最新の「初期」と最も古い「レビュー」を要求しています)

実際のコードでは、私の開始状態と終了状態はパラメーターになります

編集: おっと、「t3.At < t2.At」と「t4.At > t1.At」を含める必要があります。状態のいくつかの奇妙なシーケンスを修正するためです (たとえば、2 番目の「レビュー」を削除してから「作業」からクエリを実行した場合)。 " から "レビュー" すると、元のクエリは失敗します)

于 2008-09-17T13:33:58.590 に答える
0

多くのレコードから 1 つの結果を取得しようとしているため、1 つの SQL ステートメントでその答えを得ることはできないと思います。SQL でこれを実現する唯一の方法は、2 つの異なるレコードのタイムスタンプ フィールドを取得し、差 (datediff) を計算することです。したがって、UNIONS または内部結合が必要です。

于 2008-09-17T13:24:31.297 に答える
0
    -- Oracle SQl

    CREATE TABLE ObjectState
    (
        startdate date NOT NULL,
        state varchar2(10) NOT NULL
    );



   insert into ObjectState 
   select to_date('01-Aug-2008 13:30:00','dd-Mon-rrrr hh24:mi:ss'),'Initial' union all
   select to_date('02-Aug-2008 13:30:00','dd-Mon-rrrr hh24:mi:ss'),'Work' union all
   select to_date('03-Aug-2008 13:30:00','dd-Mon-rrrr hh24:mi:ss'),'Review' union all
   select to_date('04-Aug-2008 13:30:00','dd-Mon-rrrr hh24:mi:ss'),'Work' union all
   select to_date('05-Aug-2008 13:30:00','dd-Mon-rrrr hh24:mi:ss'),'Review' union all
   select to_date('06-Aug-2008 13:30:00','dd-Mon-rrrr hh24:mi:ss'),'Accepted' union all
   select to_date('07-Aug-2008 13:30:00','dd-Mon-rrrr hh24:mi:ss'),'Done';

-- Days in between two states

  select  o2.startdate - o1.startdate as days
  from ObjectState o1, ObjectState o2
  where o1.state = 'Initial'
  and o2.state = 'Review';
于 2008-09-17T13:59:12.040 に答える
0

あなたの歩数 (旅行の各記録は歩数として見ることができます) は、同じアクティビティの一部としてどこかにグループ化できると思います。次に、次のように、データをグループ化することができます。

SELECT Min(Tbl_Step.dateTimeStep) as tripBegin, _   
       Max(Tbl_Step.dateTimeStep) as tripEnd _
FROM 
       Tbl_Step 
WHERE 
       id_Activity = 'AAAAAAA'

この原則を使用して、アクティビティのステップ数などの他の集計を計算できます。ただし、2 つのステップ間のギャップのような値を計算する SQL の方法は見つかりません。このようなデータは最初のステップにも 2 番目のステップにも属さないからです。一部のレポート ツールは、「累計」と呼ばれるものを使用して、このような中間データを計算します。目的によっては、これが解決策になる場合があります。

于 2008-09-17T14:29:28.130 に答える
0

わかりました、これはちょっとマニアックなことではありませんが、病院に行く時間が近づいているときに職場から確認できるように、赤ちゃんが生まれる直前に妻の陣痛を追跡するための Web アプリケーションを作成しました。とにかく、この基本的なものを 2 つのビューとしてかなり簡単に作成しました。

create table contractions time_date timestamp primary key;

create view contraction_time as
SELECT a.time_date, max(b.prev_time) AS prev_time
   FROM contractions a, ( SELECT contractions.time_date AS prev_time
           FROM contractions) b
  WHERE b.prev_time < a.time_date
  GROUP BY a.time_date;

create view time_between as 
SELECT contraction_time.time_date, contraction_time.prev_time, contraction_time.time_date - contraction_time.prev_time
   FROM contraction_time;

これは明らかに副選択としても実行できますが、他の目的にも中間ビューを使用したため、これはうまくいきました。

于 2008-09-30T16:36:54.607 に答える