涉及搜索连续日期的复杂查询(按月)

时间:2012-10-25 19:01:05

标签: sql pattern-matching

我有一个表格,其中包含按月列出的帐户列表以及表示活动的字段。我想根据以下标准搜索以查找帐户何时死亡"

  1. 该帐户在连续的几个月内保持一致的活动
  2. 该帐户在最后一个月的活动激增(加价=之前所有连续月份平均值的200%或更多)
  3. 活动激增后的一个月,接下来的12个月都有0活动
  4. 所以表格看起来像这样:

    ID | Date      | Activity
    1  | 1/1/2010  | 2
    2  | 1/1/2010  | 3.2
    1  | 2/3/2010  | 3
    2  | 2/3/2010  | 2.7
    1  | 3/2/2010  | 8
    2  | 3/2/2010  | 9
    1  | 4/6/2010  | 0
    2  | 4/6/2010  | 0
    1  | 5/2/2010  | 0
    2  | 5/2/2010  | 2
    

    因此,在这种情况下,账户1和账户2在1月至3月期间都有活动。两个账户在3月份都出现了激增。两个帐户在4月都有0活动。帐户2在5月再次开展活动,但帐户1没有。因此,我的查询应返回帐户1,但不返回帐户2.我希望将此视为我的查询结果:

    ID | Last Date
    1  | 3/2/2010 
    

    我意识到这是一个复杂的问题,我不希望任何人为我编写整个查询。我能想到的当前最好的方法是创建一系列子查询并加入它们,但我甚至不知道子查询的样子。例如:如何为单个ID查找连续的一系列行,其中activity为全0(或全部为非?)。

    如果SQL过于复杂,我的后退是使用Java进行强力搜索,我首先会找到所有唯一ID,然后对每个唯一ID进行迭代,以确定是否以及何时使用ID "死亡"

    再一次:非常感谢任何朝着正确方向前进的帮助。

2 个答案:

答案 0 :(得分:0)

使用Java进行处理,或者在SQL中进行部分处理,并使用Java完成处理是一种很好的方法。

我不会解决如何定义尖峰的问题。

我建议您从条件3开始。很容易找到最后一个非零值。那就是你要测试尖峰的那个,以及尖峰之前的一致数据。

SELECT out.*
FROM monthly_activity out
  LEFT OUTER JOIN monthly_activity comp
    ON out.ID = comp.ID AND out.Date < comp.Date AND comp.Activity <> 0
WHERE comp.Date IS NULL

不错,但如果这是因为记录是本月的最后一个,那么你不想要结果,所以相反,

SELECT out.*
FROM monthly_activity out
  INNER JOIN monthly_activity comp
    ON out.ID = comp.ID AND out.Date < comp.Date AND comp.Activity == 0
GROUP BY out.ID

答案 1 :(得分:0)

可能不是世界上效率最高的代码,但我认为这可以满足您的需求:

declare @t table (AccountId int, ActivityDate date, Activity float)

insert @t 
      select 1,   '2010-01-01', 2
union select 2,   '2010-01-01', 3.2
union select 1,   '2010-02-03', 3
union select 2,   '2010-02-03', 2.7
union select 1,   '2010-03-02', 8
union select 2,   '2010-03-02', 9
union select 1,   '2010-04-06', 0
union select 2,   '2010-04-06', 0
union select 1,   '2010-05-02', 0
union select 2,   '2010-05-02', 2


select AccountId, ActivityDate LastActivityDate --, Activity
from @t a
where 
--Part 2 --select only where the activity is a peak
Activity >= isnull
(
    (
        select 2 * avg(c.Activity)
        from @t c
        where c.AccountId = 1
        and c.ActivityDate >= isnull
        (
            (
                select max(d.ActivityDate)
                from @t d
                where d.AccountId = c.AccountId
                and d.ActivityDate < c.ActivityDate
                and d.Activity = 0  
            )
            ,
            (
                select min(e.ActivityDate)
                from @t e
                where e.AccountId = c.AccountId
            )
        )
        and c.ActivityDate < a.ActivityDate
    )
    , Activity + 1 --Part 1 (i.e. if no activity before today don't include the result)
)
--Part 3
and not exists --select only dates which have had no activity for the following 12 months on the same account (assumption: count no record as no activity / also ignore current date in this assumption)
(
    select 1
    from @t b
    where a.AccountId = b.AccountId
    and b.Activity > 0
    and b.ActivityDate between dateadd(DAY, 1, a.ActivityDate) and dateadd(YEAR, 1, a.ActivityDate)
)