说我有付款表。我需要知道按人员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
)
答案 0 :(得分:3)
如果您有SQL Server 2014,可以使用LAG
或LEAD
功能查看其他行,这样可以轻松实现:
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_NUMBER
和LEFT 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