4

日付の範囲に場所がない場合の識別など、場所の日付に基づいていくつかのデータをグループ化する必要があります。範囲と場所のすべての日付のリストを作成できたという点で、私はそこにいる途中です。

  • 日付1 場所1
  • 日付2 場所1
  • 日付3 場所1
  • date4 不明
  • date5 不明
  • date6 不明
  • 日付7 場所2
  • 日付8 場所2
  • 日付9 場所2
  • 日付10 場所2
  • 日付11 場所1
  • 日付12 場所1
  • 日付13 場所1

(min(date) と max(date) を表示する) による通常のグループを使用すると、次のような結果が得られます。

  • 場所1、日付1、日付13
  • 場所2、日付7、日付10
  • 不明、date4、date6

しかし、私はこれが欲しい:

  • 場所1、日付1、日付3
  • 不明、日付4、日付6
  • 場所2、日付7、日付9
  • 場所1、日付11、日付13

また、短い範囲の Unknown を除外する必要がありますが、それは二次的なものです。

これが理にかなっているといいのですが、本当に簡単なはずです。

4

1 に答える 1

1

島とギャップの問題と Itzik Ben-gan を調べてください。必要な結果を得るには、セットベースの方法があります。

ROW_NUMBER または RANK の使用を検討していましたが、LAG と LEAD (SQL 2012 で導入) に出くわしました。以下に解決策があります。間違いなく単純化できますが、いくつかの CTE を使用すると、私の思考プロセス (欠陥があるかもしれません) が見やすくなります。ゆっくりとデータを必要なものに変換します。それぞれの新しい CTE が生成するものを確認したい場合は、一度に 1 つの選択のコメントを外します。

create table Junk
(aDate Datetime,
aLocation varchar(32))

insert into Junk values
('2000', 'Location1'),
('2001', 'Location1'),
('2002', 'Location1'),
('2004', 'Unknown'),
('2005', 'Unknown'),
('2006', 'Unknown'),
('2007', 'Location2'),
('2008', 'Location2'),
('2009', 'Location2'),
('2010', 'Location2'),
('2011', 'Location1'),
('2012', 'Location1'),
('2013', 'Location1'),
('2014', 'Location3')


;WITH StartsMiddlesAndEnds AS
(
    select
    aLocation, 
    aDate, 
    CASE(LAG(aLocation) OVER (ORDER BY aDate, aLocation)) WHEN aLocation THEN 0 ELSE 1 END [isStart],
    CASE(LEAD(aLocation) OVER (ORDER BY aDate, aLocation)) WHEN aLocation THEN 0 ELSE 1 END [isEnd]
    from Junk 
)
--select * from NumberedStartsMiddlesAndEnds
,NumberedStartsAndEnds AS --let's get rid of the rows that are in the middle of consecutive date groups
(
    select 
    aLocation,
    aDate,
    isStart,
    isEnd,
    ROW_NUMBER() OVER(ORDER BY aDate, aLocation) i
    FROM StartsMiddlesAndEnds 
    WHERE NOT(isStart = 0 AND isEnd = 0) --it is a middle row
)
--select * from NumberedStartsAndEnds
,CombinedStartAndEnds AS --now let's put the start and end dates in the same row
(
    select
    rangeStart.aLocation,
    rangeStart.aDate [aStart],
    rangeEnd.aDate [aEnd]
    FROM NumberedStartsAndEnds rangeStart
    join NumberedStartsAndEnds rangeEnd ON rangeStart.aLocation = rangeEnd.aLocation
    WHERE rangeStart.i = rangeEnd.i - 1 --consecutive rows
    and rangeStart.isStart = 1
    and rangeEnd.isEnd = 1
)
--select * from CombinedStartAndEnds
,OneDateIntervals AS --don't forget the cases where a single row is both a start and end
(
    select
    aLocation,
    aDate [aStart],
    aDate [aEnd]
    FROM NumberedStartsAndEnds
    WHERE isStart = 1 and isEnd = 1
)
--select * from OneDateIntervals
select aLocation, DATEPART(YEAR, aStart) [start], DATEPART(YEAR, aEnd) [end] from OneDateIntervals
UNION
select aLocation, DATEPART(YEAR, aStart) [start], DATEPART(YEAR, aEnd) [end] from CombinedStartAndEnds
ORDER BY DATEPART(YEAR, aStart)

そしてそれは生み出す

aLocation   start   end
Location1   2000    2002
Unknown 2004    2006
Location2   2007    2010
Location1   2011    2013
Location3   2014    2014

2012年はありませんか?その後、ROW_NUMBER を使用して同じ StartsMiddlesAndEnds CTE を取得できます。

;WITH NumberedRows AS
(
    SELECT aLocation, aDate, ROW_NUMBER() OVER (ORDER BY aDate, aLocation) [i] FROM Junk
)
,StartsMiddlesAndEnds AS
(
    select
    currentRow.aLocation, 
    currentRow.aDate, 
    CASE upperRow.aLocation WHEN currentRow.aLocation THEN 0 ELSE 1 END [isStart],
    CASE lowerRow.aLocation WHEN currentRow.aLocation THEN 0 ELSE 1 END [isEnd]
    from
    NumberedRows currentRow
    left outer join NumberedRows upperRow on upperRow.i = currentRow.i-1
    left outer join NumberedRows lowerRow on lowerRow.i = currentRow.i+1
)
--select * from StartsMiddlesAndEnds
于 2012-10-17T19:20:59.147 に答える