制作一个日历表(只做一次),这个由 sqlskull.com 提供:
DECLARE @Year INT = '2000';
DECLARE @YearCnt INT = 50 ;
DECLARE @StartDate DATE = DATEFROMPARTS(@Year, 1, 1)
DECLARE @EndDate DATE = DATEADD(DAY, -1, DATEADD(YEAR, @YearCnt, @StartDate));
;WITH Cal(n) AS
(
SELECT 0 UNION ALL SELECT n + 1 FROM Cal
WHERE n < DATEDIFF(DAY, @StartDate, @EndDate)
),
FnlDt(d) AS
(
SELECT DATEADD(DAY, n, @StartDate), n FROM Cal
),
FinalCte AS
(
SELECT
[D] = CONVERT(DATE,d),
[Dy] = DATEPART(DAY, d),
[Mo] = DATENAME(MONTH, d),
[Yr] = DATEPART(YEAR, d),
[DN] = DATENAME(WEEKDAY, d),
N
FROM FnlDt
)
SELECT *
INTO tally
FROM finalCte
ORDER BY [Date]
OPTION (MAXRECURSION 0);
现在您有了一个可以永远使用的日期和数字表,如下所示:
SELECT *
FROM
(SELECT
cust_id,
min(bill_date) minbill,
max(bill_date) maxbill
FROM t
) r
INNER JOIN tally x ON x.Dy = DAY(minbill) AND x.d BETWEEN r.minbill AND r.maxbill
LEFT JOIN t ON x.d = t.bill_date and r.cust_id = t.cust_id
内部连接到计数表的分组查询是您的交叉连接(它不能是交叉连接,因为它确实有一些谓词,即计数行介于 min 和 max 之间)。这将导致每个按客户分组的行乘以最小值和最大值之间的计数中的日期数 - 它为您提供了每个客户 ID 与最小值和最大值之间的每个日期的交叉连接。
然后,您只需使用左连接将其连接回 cust 数据表,以获取有数据的日期或没有数据的日期(并为那些没有数据的日期保留统计日期任何客户数据 - 将 null 转换为 0)