我试图查询计算某些客户的连续逾期天数。我还有一个识别客户的主键。
样本表
Date (d.m.y) Name
01.01.2014 Alex
02.01.2014 Alex
03.01.2014 Alex
01.01.2014 Bianca
02.01.2014 Bianca
08.07.2014 Alex
09.07.2014 Alex
10.07.2014 Alex
11.07.2014 Alex
如何区分名称,只计算制作SELECT COUNT()
的连续日期?
Desired Result
Name Overdue Day Count Date
Alex 3 01.01.2014 <== The date is the first overdue date
Bianca 2 01.01.2014
Alex 4 08.07.2014
答案 0 :(得分:3)
这是一个gaps and islands problem(特别是岛屿) - 不幸的是我认为访问中唯一支持的解决方案效率非常低:
SELECT Name,
COUNT(*) AS Days,
MIN(Date) AS FirstDate,
MAX(Date) AS LastDate
FROM ( SELECT Name,
Date,
( SELECT MIN(B.Date)
FROM T AS B
WHERE B.Date >= A.Date
AND B.Name = A.Name
AND NOT EXISTS
( SELECT 1
FROM T AS C
WHERE C.Name = B.Name
AND C.Date = B.Date + 1
)
) AS grp
FROM T AS A
) AS D
GROUP BY Name, grp;
上面链接的文章中有完整的解释,但用于创建列grp
的子查询找到每个特定岛的末尾,然后此值可用于对外部查询进行分组。
答案 1 :(得分:2)
使用[NOT] EXISTS
的查询可能会很慢,因此这里的解决方案可能运行得更快。
我们首先创建一个查询,按名称
查找每个连续日期组的开始日期SELECT t1.Date, t1.Name
FROM
T AS t1
LEFT JOIN
T AS t2
ON t1.Name=t2.Name
AND t1.Date=DateDiff("d",-1,t2.Date)
WHERE t2.Date IS NULL
它给了我们
Date Name
---------- ------
2014-01-01 Alex
2014-01-01 Bianca
2014-07-08 Alex
我们可以使用不等连接
将该查询链接回主表SELECT t3.Date, t3.Name
FROM
T AS t3
INNER JOIN
(
SELECT t1.Date, t1.Name
FROM
T AS t1
LEFT JOIN
T AS t2
ON t1.Name=t2.Name
AND t1.Date=DateDiff("d",-1,t2.Date)
WHERE t2.Date IS NULL
) AS StartDates
ON t3.Name=StartDates.Name AND t3.Date>=StartDates.Date
生成以下内容,为每个连续的组重复行
Date Name
---------- ------
2014-01-01 Alex
2014-01-02 Alex
2014-01-03 Alex
2014-01-01 Bianca
2014-01-02 Bianca
2014-07-08 Alex
2014-07-08 Alex
2014-07-09 Alex
2014-07-09 Alex
2014-07-10 Alex
2014-07-10 Alex
2014-07-11 Alex
2014-07-11 Alex
因此,如果我们将其调整为聚合查询,我们可以为原始表中的每一行分配一个组号
SELECT t3.Date, t3.Name, COUNT(*) AS GroupNo
FROM
T AS t3
INNER JOIN
(
SELECT t1.Date, t1.Name
FROM
T AS t1
LEFT JOIN
T AS t2
ON t1.Name=t2.Name
AND t1.Date=DateDiff("d",-1,t2.Date)
WHERE t2.Date IS NULL
) AS StartDates
ON t3.Name=StartDates.Name AND t3.Date>=StartDates.Date
GROUP BY t3.Date, t3.Name
导致
Date Name GroupNo
---------- ------ -------
2014-01-01 Alex 1
2014-01-02 Alex 1
2014-01-03 Alex 1
2014-01-01 Bianca 1
2014-01-02 Bianca 1
2014-07-08 Alex 2
2014-07-09 Alex 2
2014-07-10 Alex 2
2014-07-11 Alex 2
最后,我们可以将整个事物包装在另一个聚合查询中
SELECT
Grouped.Name,
COUNT(*) AS DaysOverdue,
MIN(Grouped.Date) AS OverdueSince
FROM
(
SELECT t3.Date, t3.Name, COUNT(*) AS GroupNo
FROM
T AS t3
INNER JOIN
(
SELECT t1.Date, t1.Name
FROM
T AS t1
LEFT JOIN
T AS t2
ON t1.Name=t2.Name
AND t1.Date=DateDiff("d",-1,t2.Date)
WHERE t2.Date IS NULL
) AS StartDates
ON t3.Name=StartDates.Name AND t3.Date>=StartDates.Date
GROUP BY t3.Date, t3.Name
) AS Grouped
GROUP BY Grouped.Name, Grouped.GroupNo
ORDER BY 3, 1
产生最终结果
Name DaysOverdue OverdueSince
------ ----------- ------------
Alex 3 2014-01-01
Bianca 2 2014-01-01
Alex 4 2014-07-08