3

日付のリストがあります。

互いに6か月以内にあるレコードの最大数を選択できる必要があります。

次に、すべてのレコードが選択されるまで、次に多いレコード数というように続きます。

これがデータです

1  19-Oct-2007
2  03-Dec-2007
3  16-Oct-2009
4  26-Oct-2009
5  30-Oct-2009
6  01-Nov-2009
7  16-Nov-2009
8  30-Nov-2009
9  11-Dec-2009
10  25-Dec-2009
11  01-Jan-2010
12  21-Jan-2010
13  27-Jan-2010
14  28-Jan-2010
15  28-Jan-2010
16  12-Feb-2010
17  12-Feb-2010
18  27-Feb-2010
19  09-Mar-2010
20  22-Mar-2010
21  26-Mar-2010
22  01-Apr-2010
23  22-Oct-2010
24  15-Oct-2011
25  18-Oct-2011
26  26-Oct-2011
27  16-Nov-2011
28  18-Nov-2011
29  19-Nov-2011
30  26-Nov-2011
31  29-Nov-2011
32  29-Nov-2011
33  30-Nov-2011
34  06-Dec-2011
35  16-Dec-2011
36  17-Dec-2011
37  20-Dec-2011
38  28-Dec-2011
39  01-Jan-2012
40  01-Jan-2012
41  09-Jan-2012
42  13-Jan-2012
43  27-Jan-2012
44  01-Feb-2012
45  23-Feb-2012
46  29-Feb-2012
47  01-Mar-2012
48  01-Mar-2012
49  01-Mar-2012
50  02-Mar-2012
51  04-Mar-2012
52  04-Mar-2012
53  05-Mar-2012
54  05-Mar-2012
55  17-Mar-2012
56  23-Mar-2012
57  24-Mar-2012
58  01-Apr-2012
59  03-Apr-2012
60  04-Apr-2012

考えられる解決策の1つは、

  • 記録24-60(それらは互いに172日以内にあります)
  • レコード23(前/次の日付から6か月以内ではない)
  • 記録3-22(それらは互いに167日以内にあります)
  • レコッド1-2(お互いに45日以内)

(私は最大の日付から開始し、逆方向に選択しました。これはおそらく最適なソリューションではありません)

4

4 に答える 4

2

以下は、この問題に対する反復的なアプローチであり、現在のところ、それよりも優れた提案はありません。ただし、動作するはずです:

WITH ranked AS (
  SELECT *, rnk = ROW_NUMBER() OVER (ORDER BY Date DESC)
  FROM data
),
marked AS (
  SELECT
    rnk,
    Date,
    GroupDate = date
  FROM ranked
  WHERE rnk = 1
  UNION ALL
  SELECT
    r.rnk,
    r.Date,
    GroupDate = CASE
      WHEN m.GroupDate > DATEADD(MONTH, 6, r.Date) THEN r.Date
      ELSE m.GroupDate
    END
  FROM ranked r
  INNER JOIN marked m ON r.rnk = m.rnk + 1
)
SELECT
  MinDate     = MIN(Date),
  MaxDate     = MAX(Date),
  [RowCount]  = COUNT(*),
  RangeLength = DATEDIFF(DAY, MIN(Date), MAX(Date))
FROM marked
GROUP BY
  GroupDate
ORDER BY
  GroupDate

あれは、

  1. 最後の日付が取得され、範囲チェックとグループ マーカーの両方に使用されます。

  2. 以降の (前の) 日付は、マーカーが検出されるより半年以上前になるまで処理されます。

  3. 見つかった日付が新しいグループ マーカーになり、行がなくなるまでプロセスが手順 1 から続行されます。

反復に進む前に、行がランク付けされます。ただし、ギャップのない一意の連続値を含むことが保証されている列がある場合は、ランキング番号の代わりにその列を使用できます。

元の投稿のサンプルの結果は次のとおりです。

MinDate     MaxDate     RowCount     RangeLength
----------  ----------  -----------  -----------
2007-10-19  2007-12-03  2            45
2009-10-16  2010-04-01  20           167
2010-10-22  2010-10-22  1            0
2011-10-15  2012-04-04  37           172

セットアップを含むスクリプト全体は、 SQL Fiddleで見つけて再生できます。

于 2012-04-12T11:11:23.017 に答える
1

私は自分のテストデータを使用しましたが、これは非常に複雑なものです。カーソルを使った方が扱いやすいかもしれません。しかし、私はカーソルの大ファンではありません。私はそれに最善を尽くしました:

declare @t table(record int, date datetime)
insert @t values(1,'19-Oct-2007'),
(2,'03-Dec-2007'),
(3,'2-may-2009'),
(4,'16-Oct-2009'),
(5,'26-Oct-2009'),
(6,'30-Oct-2009'),
(7,'01-Nov-2009'),
(8,'16-Nov-2009'),
(9,'30-Nov-2009'),
(10,'11-Dec-2009'),
(11,'11-Dec-2010'),
(12,'11-Dec-2010'),
(13,'11-Dec-2010')

;with a as
(
  select datediff(day, t1.date, t2.date) daysapart, 
  row_number() over (order by count desc) rn,
  b.count, 
         t1.record fromrecord, 
         t2.record torecord
  from @t t1
  join @t t2
  on t1.date <= t2.date 
     and dateadd(month, 6, t1.date) > t2.date 
     and t1.record <= t2.record
  cross apply (select count(*) count from @t where record between t1.record and t2.record) b
)
, b as
(
    select * from a where not exists 
    (select 1 from a b where (a.fromrecord between b.fromrecord and b.torecord
      or a.torecord between b.fromrecord and b.torecord)
      and a.rn > b.rn and not exists(select 1 from a c where 
      (b.fromrecord between c.fromrecord and c.torecord
      or b.torecord between c.fromrecord and c.torecord)
      and b.rn > c.rn))
)
select count, fromrecord, torecord, daysapart from b

結果:

count       fromrecord  torecord    daysapart
----------- ----------- ----------- -----------
7           4           10          56
3           11          13          0
2           1           2           45
1           3           3           0
于 2012-04-12T08:31:05.747 に答える
1
select d1.date, count(*)
from dates as d1 with (nolock) 
join dates as d2 with (nolock) 
on datediff(mm,d2.date,d1.date) < 6 
group by d1.date  
order by count(*) desc 
于 2012-04-11T22:15:01.240 に答える
0

私は考えました

  1. 6か月=180日
  2. 日付の差が180日を超える場合は、リストに表示されます。

これを試して :

create table #list (id int, dt datetime  )
-- insert you data into #list

select s1.id as ID_1, s1.dt as Date_2 , s2.id as ID_2, s2.dt as Date_2 
,abs( datediff(day, s2.dt, s1.dt) ) diff_in_days
from #list s1 , #list s2 
order by  case when abs(datediff(day, s2.dt, s1.dt) ) > 180 then 1
else  abs(datediff(day, s2.dt, s1.dt)) end  desc 
于 2012-04-12T05:36:35.237 に答える