2011-01-10 59 views
1

私は以下のものを持っています。T-SQLで開始日と終了日を設定します。

Name Date 
A 2011-01-01 01:00:00.000 
A 2011-02-01 02:00:00.000 
A 2011-03-01 03:00:00.000 
B 2011-04-01 04:00:00.000 
A 2011-05-01 07:00:00.000 

所望の出力は、セットベースのアプローチで同じ使用TSQLを達成する方法

Name  StartDate      EndDate 
------------------------------------------------------------------- 
A   2011-01-01 01:00:00.000   2011-04-01 04:00:00.000  
B   2011-04-01 04:00:00.000   2011-05-01 07:00:00.000  
A   2011-05-01 07:00:00.000   NULL 

あります。

DECLARE @t TABLE(PersonName VARCHAR(32), [Date] DATETIME) 
INSERT INTO @t VALUES('A', '2011-01-01 01:00:00') 
INSERT INTO @t VALUES('A', '2011-01-02 02:00:00') 
INSERT INTO @t VALUES('A', '2011-01-03 03:00:00') 
INSERT INTO @t VALUES('B', '2011-01-04 04:00:00') 
INSERT INTO @t VALUES('A', '2011-01-05 07:00:00') 

Select * from @t 
+0

希望の出力をどのように計算しているのか分かりません。どのように終了日を決定するのですか?たとえば、希望する出力にレコードがあります。 名前開始日終了日 A 2011-01-01 01:00:00.000 2011-04-01 04:00:00.000 入力は、 04-01 04:00:00.000 B.名前とレコードの終了日を決定するにはどうすればよいですか?レコードの定義は何ですか? – richard

+0

レコードの終了日は、別のレコードの開始日です。 Aの開始日は2011-01-01 01:00:00.000ですが、Bは2011-04-01 04:00:00.000です。したがって、Aの終了日は2011-04-01 04:00:00.000です。同様に、Bに続くAは、Bの終了日である2011-05-01 07:00:00.000として開始日を持っています。 –

+0

しかし、どのレコードを特定のレコードの終了日としてどのように知ることができますか? – richard

答えて

0

下CTEを有する他の答えは良いものであるよう

DDLです。もう1つの選択肢は、どのような場合でもコレクションを反復することです。これは基本的なものではありませんが、それを行う別の方法です。

A.トランザクションに対応する各レコードに一意のIDを割り当てるか、または実際に出力を得るためにB.に反復する必要があります。

TSQLはレコードを繰り返し処理するのには理想的ではありません。特に、たくさんある場合は、小さなネットのプログラムやイテレーションの方が良い方法をお勧めします。

7
;WITH cte1 
    AS (SELECT *, 
       ROW_NUMBER() OVER (ORDER BY Date) - 
       ROW_NUMBER() OVER (PARTITION BY PersonName 
       ORDER BY Date) AS G 
     FROM @t), 
    cte2 
    AS (SELECT PersonName, 
       MIN([Date]) StartDate, 
       ROW_NUMBER() OVER (ORDER BY MIN([Date])) AS rn 
     FROM cte1 
     GROUP BY PersonName, 
        G) 
SELECT a.PersonName, 
     a.StartDate, 
     b.StartDate AS EndDate 
FROM cte2 a 
     LEFT JOIN cte2 b 
     ON a.rn + 1 = b.rn 

のCTEの結果は、一般的しかし あなたも、あなたが以下のように 中間結果を自分で具現場合は、より良いパフォーマンスを得るかもしれませんがマテリアライズされていませんので。

DECLARE @t2 TABLE (
    rn   INT IDENTITY(1, 1) PRIMARY KEY, 
    PersonName VARCHAR(32), 
    StartDate DATETIME); 

INSERT INTO @t2 
SELECT PersonName, 
     MIN([Date]) StartDate 
FROM (SELECT *, 
       ROW_NUMBER() OVER (ORDER BY Date) - 
       ROW_NUMBER() OVER (PARTITION BY PersonName 
       ORDER BY Date) AS G 
     FROM @t) t 
GROUP BY PersonName, 
      G 
ORDER BY StartDate 

SELECT a.PersonName, 
     a.StartDate, 
     b.StartDate AS EndDate 
FROM @t2 a 
     LEFT JOIN @t2 b 
     ON a.rn + 1 = b.rn 
0

前のレコードの場所を知るために行番号を取得します。次に、レコードとその次のレコードを取る。状態が変わると候補行があります。それは、そのようなことは、異なるPersonNameに関連付けされた後

select 
    state, 
    min(start_timestamp), 
    max(end_timestamp) 

from 
(
    select 
     first.state, 
     first.timestamp_ as start_timestamp, 
     second.timestamp_ as end_timestamp 

     from 
     (
      select 
       *, row_number() over (order by timestamp_) as id 
      from test 
     ) as first 

     left outer join 
     (
      select 
       *, row_number() over (order by timestamp_) as id 
      from test 
     ) as second 
     on 
      first.id = second.id - 1 
      and first.state != second.state 
) as agg 
group by state 
    having max(end_timestamp) is not null 

union 

-- last row wont have a ending row 
--(select state, timestamp_, null from test order by timestamp_ desc limit 1) 
    -- I think it something like this for sql server 
    (select top state, timestamp_, null from test order by timestamp_ desc) 

order by 2 
; 

は、PostgreSQLでテストが、

0
SELECT 
    PersonName, 
    StartDate = MIN(Date), 
    EndDate 
FROM (
    SELECT 
    PersonName, 
    Date, 
    EndDate = (
     /* get the earliest date after current date 
     associated with a different person */ 
     SELECT MIN(t1.Date) 
     FROM @t AS t1 
     WHERE t1.Date > t.Date 
     AND t1.PersonName <> t.PersonName 
    ) 
    FROM @t AS t 
) s 
GROUP BY PersonName, EndDate 
ORDER BY 2 

基本的には同様にSQL Serverで動作するはずです、すべてのDateのために、私たちは、最寄りの日付を見つけます。これは私たちにEndDateを与え、同じ人物の連続したグループのグループを区別します。

今、私たちは唯一のPersonName & EndDateによってグループにデータを必要とStartDateとして各グループでの最小限のDateを取得します。もちろん、StartDateでデータを並べ替えてください。

関連する問題