【问题标题】:Select the largest number of records based on a date range. (that have dates within 6 months of eachother)根据日期范围选择最大数量的记录。 (彼此的日期在 6 个月内)
【发布时间】:2012-04-13 13:45:02
【问题描述】:

我有一个日期列表。

我需要能够选择彼此相距 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

一种可能的解决方案是选择

  • 记录 24 到 60 条(它们在 172 天内)
  • 记录 23(其不在前/后日期的 6 个月内)
  • 记录 3-22(彼此相距在 167 天内)
  • 记录 1-2(他们在 45 天内)

(我从最大的日期开始,向后选择。这可能不是最佳解决方案)

【问题讨论】:

    标签: sql-server tsql sql-server-2005


    【解决方案1】:

    以下是该问题的迭代方法,目前我没有比这更好的建议了。不过它应该可以工作:

    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
    

    可以使用on SQL Fiddle 找到和播放整个脚本,包括设置。

    【讨论】:

    • 对不起,我写的太早了。这是不正确的。不过,这是一个很好的尝试。
    • @t-clausen.dk:我不确定我是否理解这个解决方案的问题,但我很想尝试解决它,特别是因为你已经支持了我的回答.
    • 用这些日期试试你的查询,应该很清楚:(1, '2012-01-01'), (2, '2012-01-01'), (3, '2012- 03-01'), (4, '2012-03-01'), (5, '2012-08-01')
    • @t-clausen.dk:谢谢。我看不出我得到的结果有什么问题,但后来我想也许我们对问题的理解不同。我想我现在明白了你是如何理解它的(这可能比我做的更正确)。您认为查询必须找到彼此相距 6 个月内的最大日期子集,不是吗?原始帖子中的一些线索,我必须首先(dis)错过,确实支持这种观点。我认为可以使用递归 CTE 来找到 one (最大的)这样的子集。但到目前为止找到所有这些似乎是一项复杂得多的任务。
    • 我同意这很复杂。这正是我在查询中尝试过的,它似乎有效。
    【解决方案2】:
    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 
    

    【讨论】:

    • @t-clausen.dk 我在 SQL 2008 R2 上对其进行了测试。哪个部分不适合您?
    • 改正语法后,只有结果错误。尝试来自@andryM 的脚本
    【解决方案3】:

    我使用了自己的测试数据,这是非常复杂的东西。使用光标可能更容易处理。但我不是游标的忠实粉丝。我已经尽力了:

    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
    

    【讨论】:

    • 这有点令人兴奋,但它确实有效!我对您的解决方案的一个(相当小的)挑剔是这种情况:datediff(day, t1.date, t2.date) between 0 and 180。我真的会把它改成这样:t2.date &lt;= dateadd(month, 6, t1.date)。当然,除了将范围更精确地设置为 6 个月外,它没有任何改变。很棒的解决方案,而且似乎也是唯一真正解决问题的解决方案。
    • @AndriyM 感谢您的评论和好话。很好地发现了日期问题。我刚醒来并登录以更改它,因为它困扰着我。否则我永远不会这么早起床
    • 顺便说一句。这不适用于大型数据集
    【解决方案4】:

    我考虑过

    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 
    

    【讨论】:

    • 我试过你的脚本,但它没有给出正确或远程正确的结果。这是对我之前评论的更正
    • @t-clausen.dk 你能告诉我 1> 在问题的给定数据集中,我的第一行输出应该是什么?
    • 对我来说,理解是 - 在数据集中找到 6M 中的日期之间的差异,然后按降序排列。
    • 所以你假设作者想要 3600 行作为结果集?
    • 不知道……你能说清楚他想要什么吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-04
    相关资源
    最近更新 更多