2011-01-06 12 views
4

私は、価値の変化の間にある時間を見つける必要がある状況があります。私は単純なgroup by節を試しましたが、重複する変更を排除します。次の例を考えてみましょう:グループ化句を使用するときに日付範囲が重複しないようにするにはどうすればよいですか?

create table #items (
     code varchar(4) 
    , class varchar(4) 
    , txdate datetime 
) 

insert into #items (code, class, txdate) values ('A', 'C', '2010-01-01'); 
insert into #items (code, class, txdate) values ('A', 'C', '2010-01-02'); 
insert into #items (code, class, txdate) values ('A', 'C', '2010-01-03'); 
insert into #items (code, class, txdate) values ('A', 'D', '2010-01-04'); 
insert into #items (code, class, txdate) values ('A', 'D', '2010-01-05'); 
insert into #items (code, class, txdate) values ('A', 'C', '2010-01-06'); 
insert into #items (code, class, txdate) values ('A', 'C', '2010-01-07'); 
insert into #items (code, class, txdate) values ('A', 'D', '2010-01-08'); 
insert into #items (code, class, txdate) values ('A', 'D', '2010-01-09'); 

select code 
, class 
, min(txdate) mindate 
, max(txdate) maxdate 
from #items 
group by code, class 

これは、次のような結果(重複日付範囲に注意してください)を返します:

|code|class|mindate |maxdate | 
---------------------------------- 
|A |C |2010-01-01|2010-01-07| 
|A |D |2010-01-04|2010-01-09| 

を私は、クエリは次のように返しがしたい:

|code|class|mindate |maxdate | 
---------------------------------- 
|A |C |2010-01-01|2010-01-03| 
|A |D |2010-01-04|2010-01-05| 
|A |C |2010-01-06|2010-01-07| 
|A |D |2010-01-08|2010-01-09| 

任意のアイデアや提案?

+0

あなたの入力日付はすべて1月に月がありますが、結果の一部には4月があります。ここに置いたときにあなたの結果に数ヶ月と数日を転載しましたか? – CanSpice

+0

@CanSpice:私の日付はyyyy-mm-dd形式を使用しています。私はエラーをキャッチし、それを修正しました。 Thx –

+0

さて、4月の日付を入力する必要があります。入力するのは1月の日付なので4月の日付です。 – CanSpice

答えて

0

追加のクラスコードがデータセットに追加されたとき、私にはよく働くようで、次のクエリを思い付きました。

select a.code, a.class, a.txdate as mindate, b.txdate as maxdate 
from (
    --Find minimum island 
    select code 
     , class 
     , txdate 
     , row_number() over (order by code, class, txdate) as n 
    from #items tb1 
    where not exists (
     select * 
     from #items tb2 
     where datediff(d, tb1.txdate, tb2.txdate) = -1  
      and tb1.class = tb2.class 
      and tb1.code = tb2.code 
    ) 
) as a 
inner join (
    --Find maximum island 
    select code 
     , class 
     , txdate 
     , row_number() over (order by code, class, txdate) as n 
    from #items tb1 
    where not exists (
     select * 
     from #items tb2 
     where datediff(d, tb1.txdate, tb2.txdate) = 1 
      and tb1.class = tb2.class 
      and tb1.code = tb2.code 
    ) 
) as b on a.n = b.n 

このアプローチの唯一の注意点は、最小セット内のエントリの数が最大セット内のエントリの数と一致する必要があることです。これまでのところ、私はこれを真実にしない何かをすることができませんでした。しかし、私はnull値やパフォーマンスをテストしませんでした。

0

私はあなたが単純なselect文でこれを行うことはできないと思います。

カーソルを使用して行を繰り返し、「クラス」の変更を識別できます。

2

編集:コメントで指摘したように、これはまだ正しくはありません。

;with cteNtile as (
    select code, class, txdate, 
      ntile((select count(*) from (select NULL as dummy from #items group by code, class) a)) over(partition by code, class order by txdate) as tilenum 
     from #items 
) 
select code, class, MIN(txdate) as mindate, MAX(txdate) as maxdate 
    from cteNtile 
    group by code, class, tilenum 
    order by mindate, maxdate 
+0

+1:正しい出力を返します。とにかくこれを行うには、最初のレコードカウントなしで? –

+0

@k rey:私がまだ思いついたわけではありません。 –

+0

+1、ローカル変数を削除して、次の行を使用することができます: 'ntile((select count(*)from(コード、クラスで@itemsグループからダミーとしてNULLを選択)a))' –

2

ここでは、目的の結果が得られるクエリを示します。 @KMにより示唆されるようSQL SERVER ISLANDSを研究した後

;WITH items1 AS (
SELECT ROW_NUMBER() OVER (ORDER BY txdate) rowid, code, class, txdate 
from #items 
), 
items2 AS (
SELECT ROW_NUMBER() OVER (ORDER BY rowid) id, rowid, i1.Code, i1.Class, i1.txdate 
FROM items1 i1 
WHERE NOT EXISTS (SELECT 1 FROM items1 i2 
        WHERE i2.txdate < i1.txdate 
        AND i2.class = i1.class 
        AND i2.Code = i1.Code 
        AND i2.rowid+1=i1.rowid) 
) 
SELECT items2.code, items2.class, items2.txdate mindate, items1.txdate maxdate 
FROM items2, items2 items3, items1 
WHERE (items2.id+1=items3.id AND items3.rowid-1=items1.rowid) 
OR items2.rowid = (SELECT MAX(t.rowid) FROM items1 t) 
UNION 
SELECT items2.code, items2.class, MAX(items2.txdate) mindate, MAX(items1.txdate) maxdate 
FROM items2, items1 
WHERE items1.class = items2.class 
GROUP BY items1.class, items2.class, items2.code, items2.class 
ORDER BY items2.txdate 
+0

+1:これは与えられた結果セットで機能します。ただし、追加のクラスコードの組み合わせを追加すると出力が乱れることがあります。 –

+0

@k rey:ok ..私は理由がある。 (コード:Bとクラス:D)のようなデータでは機能しませんでした。私はコード間の比較を一つも見逃していました。私はそれに応じて私のクエリを変更しました。それがあなたのために今働くかどうか私に知らせてください.. –

関連する問題