在SQL Server中按30天(不是按日期范围)的任何范围对数据进行分组

时间:2011-04-10 06:35:30

标签: sql sql-server tsql

我得到了一张包含交易清单的表格。 例如,假设它有4个字段: ID,UserID,DateAddedd,金额

我想运行一个查询,检查是否有时间,在30天内,用户进行的交易总数为100或更多

我看到很多按月或一天分组的样本,但问题是,例如 一个用户在20/4上进行了50美元的交易,而在5/5他做了另一个50美元的交易,查询应该显示它。 (30天内100美元或以上)

2 个答案:

答案 0 :(得分:2)

我认为这应该有效(我假设交易有一个日期组件,并且用户可以在一天内拥有多个交易):

;with DailyTransactions as (
    select UserID,DATEADD(day,DATEDIFF(day,0,DateAdded),0) as DateOnly,SUM(Amount) as Amount
    from Transactions group by UserID,DATEADD(day,DATEDIFF(day,0,DateAdded),0)
), Numbers as (
    select ROW_NUMBER() OVER (ORDER BY object_id) as n from sys.objects
), DayRange as (
    select n from Numbers where n between 1 and 29
)
select
    dt.UserID,dt.DateOnly as StartDate,MAX(ot.DateOnly) as EndDate, dt.Amount + COALESCE(SUM(ot.Amount),0) as TotalSpend
from
    DailyTransactions dt
        cross join
    DayRange dr
        left join
    DailyTransactions ot
        on
            dt.UserID = ot.UserID and
            DATEADD(day,dr.n,dt.DateOnly) = ot.DateOnly
group by dt.UserID,dt.DateOnly,dt.Amount
having dt.Amount + COALESCE(SUM(ot.Amount),0) >= 100.00

好的,我正在使用3个常用表表达式。第一个(DailyTransactions)将每个用户每天的事务表减少到一个事务(如果DateAdded只是一个日期,并且每个用户每天只有一个事务,则不需要这样做)。第二个和第三个(Numbers和DayRange)有点作弊 - 我想让数字1-29可供我使用(用于DATEADD)。创建永久或(如本例中)临时Numbers表有多种方法。我只选了一个,然后在DayRange中,我将其过滤到我需要的数字。

现在我们有了可用的那些,我们编写了主查询。我们正在查询DailyTransactions表中的行,但我们希望在同一个表中查找30天内的后续行。这就是DailyTransactions的左连接正在做的事情。它找到了后来的行,其中可能有0,1或更多。如果它不止一个,我们想要将所有这些值加在一起,这就是为什么我们需要在这个阶段进行更多的分组。最后,我们可以编写我们的having子句,仅过滤那些结果,其中特定日期的金额(dt.Amount)+晚些时候的金额总和(SUM(ot.Amount))符合您设置的条件进行。

我的基础是这样定义的表:

create table Transactions (
    UserID int not null,
    DateAdded datetime not null,
    Amount decimal (38,2)
)

答案 1 :(得分:1)

如果我理解正确,您需要一张日历表,然后检查日期和日期之间的总和+30。因此,如果你想检查一年的时间,你需要检查365个时期。

这是一种方法。递归CTE创建日历,交叉应用计算CalDate和CalDate + 30之间每个CalDate的总和。

declare @T table(ID int, UserID int, DateAdded datetime, Amount money)

insert into @T values(1, 1, getdate(),    50)
insert into @T values(2, 1, getdate()-29, 60)
insert into @T values(4, 2, getdate(),    40)
insert into @T values(5, 2, getdate()-29, 50)
insert into @T values(7, 3, getdate(),    70)
insert into @T values(8, 3, getdate()-30, 80)
insert into @T values(9, 4, getdate()+50, 50)
insert into @T values(10,4, getdate()+51, 50)

declare @FromDate datetime
declare @ToDate datetime

select
  @FromDate = min(dateadd(d, datediff(d, 0, DateAdded), 0)),
  @ToDate = max(dateadd(d, datediff(d, 0, DateAdded), 0))
from @T  

;with cal as
(
  select @FromDate as CalDate
  union all
  select CalDate + 1
  from cal
  where CalDate < @ToDate
)
select S.UserID
from cal as C
  cross apply
    (select 
       T.UserID, 
       sum(Amount) as Amount
     from @T as T
     where T.DateAdded between CalDate and CalDate + 30
     group by T.UserID) as S
where S.Amount >= 100
group by S.UserID     
option (maxrecursion 0)