SQL计数记录之间的差距为90天

时间:2015-07-30 22:46:36

标签: sql

说我有付款表。我需要知道按人员ID分组的付款间隔大于90天的次数。付款频率各不相同没有预期的付款数量。在90天内可以支付0或数百笔款项。如果一年没有付款,则计为1.如果每个月都有付款,则计数为0.如果第一个月有4笔付款,那么是90天的差额,那么还有2笔付款,那么另外90天的差距,计数将是2。

CREATE TABLE Payments
(
    ID int PRIMARY KEY,
    PersonID int FOREIGN KEY REFERENCES Persons(ID),
    CreateDate datetime
)

2 个答案:

答案 0 :(得分:3)

如果您有SQL Server 2014,可以使用LAGLEAD功能查看其他行,这样可以轻松实现:

Select PersonId, Sum(InfrequentPayment) InfrequentPayments
from
(
    select PersonId
    , case 
        when dateadd(day,@period,paymentdate) < coalesce(lead(PaymentDate) over (partition by personid order by PaymentDate),getutcdate()) 
        then 1 
        else 0 
      end InfrequentPayment
    from @Payment
) x
Group by PersonId

演示:http://sqlfiddle.com/#!6/9eecb7d/491

<强>解释

外部SQL非常简单;我们采用内部SQL的结果,按PersonId分组,并计算/总计他们支付的次数被判断为不频繁。

内部SQL也很简单;我们选择每条记录,记下该人,以及该支付(或者更确切地说是该支付后的延迟)是否经常被判断。

案例陈述确定了不常付款的构成。 我们在这里说,如果记录的付款日期加上90天仍然早于下一次付款(或当前日期,如果它是最后一次付款,那么没有下次付款)那么它&#39 ; s罕见(1);否则它不是(0)。

coalesce只是处理一个人的最后一条记录;即如果没有下一笔付款,则使用当前日期(从而捕获最后一次付款超过90天的任何人)。

现在为&#34;聪明&#34;位:lead(PaymentDate) over (partition by personid order by PaymentDate)LEAD是一个新的SQL函数,它允许您查看当前记录之后的记录(LAG是否可以查看上一条记录)。
如果您熟悉row_number()rank(),您可能已经了解此处发生了什么。 要确定当前记录之后的记录,我们不要查看当前查询;相反,我们只为这个函数指定一个order by子句;这是over关键字后面括号中的内容。 我们还希望仅将每个人的付款日期与其他付款进行比较;不是任何客户。为此,我们使用partition by子句。

我希望这有意义/符合您的要求。如果有任何不清楚的地方请说明,我会尝试改进我的解释。

修改

对于旧版本的SQL,使用或ROW_NUMBERLEFT OUTER JOIN可以达到相同的效果;即。

;with cte (PersonId, PaymentDate, SequenceNo) as
(
    select PersonId
    , PaymentDate
    , ROW_NUMBER() over (partition by PersonId order by PaymentDate)
    from @Payment
)
select a.PersonId
, sum(case when dateadd(day,@period,a.paymentdate) < coalesce(b.paymentdate,getutcdate()) then 1 else 0 end) InfrequentPayments
from cte a
left outer join cte b 
on b.PersonId = a.PersonId 
and b.SequenceNo = a.SequenceNo + 1
Group by a.PersonId

另一种适用于大多数数据库的方法(虽然效率较低)

select PersonId
, sum(InfrequentPayment) InfrequentPayments
from 
(
    select PersonId
    , case when dateadd(day,@period,paymentdate) < coalesce((
        select min(PaymentDate) 
        from @Payment b 
        where b.personid = a.personid
        and b.paymentdate > a.paymentdate
    ),getutcdate()) then 1 else 0 end InfrequentPayment
    from @Payment a
) x
Group by PersonId

答案 1 :(得分:0)

给定timestamp字段的此问题的通用查询将是这样的:

SELECT p1.personID, COUNT(*)
FROM payments p1
JOIN payments p2 ON
  p1.timestamp < p2.timestamp
  AND p1.personID = p2.personID
  AND NOT EXISTS (-- exclude combinations of p1 and p2 where p exists between them
   SELECT * FROM payments p 
   WHERE p.personID = p1.personID
   AND p.timestamp > p1.timestamp 
   AND p.timestamp < p2.timestamp)
WHERE
  DATEDIFF(p2.timestamp, p1.timestamp) >= 90
GROUP BY p1.personID