0

私は次のようなデータを持っています:

データサンプル

私がする必要があるのは、同じレコードの場合、nullClientIdではない連続した行を(CpIdを使用して)PlaceIdグループ化し、各グループの最初と最後の行を見つけて、DateAdmitted最初の行から値を取得できるようにする必要があります。DateDischarged最後の行の値。したがって、上記のデータは次のように整理してから、必要な値でフィルタリングする必要があります。

ここに画像の説明を入力してください

上記の例を使用して、私は以下に基づいて欲しいと思いますClientId

ClientId    FirstCpIdInSet    DateAdmitted    LastCpIdInSet    DateDischarged
-----------------------------------------------------------------------------
1967        NULL              NULL            NULL             NULL
1983        45                1986-12-29      45               1987-10-09
1983        47                1990-10-01      49               2009-04-12
1983        52                2009-08-31      52               2009-11-30
1988        62                1997-12-15      65               2000-01-07

ClientIdPlaceId1967は、 nullでない行がないため、結果セットから除外できます。注意すべき他のいくつかのこと:

  • CpIdこれは、として作成された一時テーブルから取得され、IDENTITYテーブルにはstrictが入力されているORDER BYためCpId、必要な順序で順番に並べられます。
  • があり、単一の行PlaceIdが連続している行の場合、は前の行のと同じClientIdである必要があります。DateAdmittedDateDischarged

できればカーソルなしでこれができるようにしたいと思っていますが、2日間困惑した後、理解できません。これはSQLServer2008R2にあります。

4

2 に答える 2

2

確かに厄介なクエリ。ほとんどのSQLの問題と同様に、問題のさまざまな側面に正しい順序で取り組むことになります。私のソリューションはカーソルを使用しません。それは外部適用とパーティション化を使用します。

実現:row_number()over(partition by xx order by yy)自体は機能しません。これは、yyが通常複数のxxパーティションにまたがっているためです。

データ例:

id  state
1   a
2   a
3   b
4   c

望ましい範囲:

1 <= x < 3
3 <= x < 4
4 <= x

ステップ1-外部適用を使用して、すべての行の次の状態遷移を見つけます。これにより、必要な基準に基づいて各行の次の値を確認できます。この手順により、必要以上の情報が生成される可能性があります。複数の行が同じ値に遷移する可能性があります。この例では、ID1と2はID3で遷移します。

擬似コード:

select t1.id, t1.state, t3.id, t3.state
from table1 t1
outer apply
(
  select 
    --only grab one row
    top 1 t2.id, t2.state
  from table1 t2 
  where 
    --grab a value that's generated after the current value.
    t1.id < t2.id 
    -- add whatever join logic you need for your case.
    and t1.memberid=t2.memberid 
    -- make sure you get the correct order, typically an identity or time
    order by t2.id asc
) T3 

このクエリは次のようなものを生成します:

id  state id    state
1   a     3     b
2   a     3     b
3   b     4     c
4   c     null  null

id=2の行は必要ありません。

ステップ2-遷移列によるパーティション化により、状態遷移が発生したときに常に1になる行番号値を取得できます。1でフィルタリングするだけで、状態遷移が得られます。

初期結果:

row_number  id  state   id  state
1           1   a       3   b
2           2   a       3   b
1           3   b       4   c
1           4   c      null null

フィルタリングされた結果:

row_number  id  state   id  state
1           1   a       3   b
1           3   b       4   c
1           4   c      null null
于 2017-01-04T23:12:40.063 に答える
1

あなたは最初と最後に何を基にしているのかを言いません。CPIDだとしましょう。あなたはランキング機能でこれを行うことができます:

select ClientID, PlaceId,
       max(CpID) as max(CPId),
       min(case when seqnumasc = 1 then DateAdmitted end) as DateAdmitted,
       max(case when seqnumdesc = 1 then DateDischarged end) as DateDischarged
from (select t.*,
             row_number() over (partition by clientID, placeID order by cpid) as seqnumasc
             row_number() over (partition by clientID, placeID order by cpid desc) as seqnumdesc
      from t
     ) t
where placeID is not null
group by ClientID, placeID

これにより、nubmerが順番に配置され、各グループの最初と最後の行が決定されます。しかし、なぜあなたは、追加された日付と排出された日付の最小値と最大値を使用できないのですか?

強化された情報に基づいています。。。

ここで問題は、次の条件に従ってレコードの「セット」を定義することであるように見えます。

  • 連続したCPID
  • 同じクライアント、同じ会社
  • nullではない場所

もしそうなら、以下はあなたに「セットID」を与えるでしょう。これは、CPIDから連続番号を減算することに基づいて、連続する値をまとめるためのトリックを使用します。この差は連続する値の定数であり、セットIDを提供します。

select clientid, setid,
       min(DateAdmitted) as DateAdmitted,
       max(DateDischarged) as DateDischarged,
       min(cpid) as minCPID,
       max(cpid) as maxCPID
from (select clientid, setid, cpid,
             row_number() over (partition by clientid, setid order by cpid) as seqnum,
             count(*) over (partition by clientid, setid) as setsize
      from (select t.*,
                   (cpid - row_number() over (partition by clientid order by cpid)
                   ) as setid
            from t
            where PlaceID is not NULL
           ) t
    ) t
group by clientid, setid
于 2012-09-10T21:28:46.583 に答える