SQL:如何将多个查询合并并判断连续计费月份
数据小许
结论
先把每条计费记录统一转换为对应月份,再按客户统计不同计费月份的数量。如果从最早月份到最晚月份应包含的月份数与实际计费月份数相等,则计费月份连续,结果为 Concurrent;否则为 Non-Concurrent。
这里有两个前提:month 能转换为真实日期,并且查询中只包含已计费记录。不要直接根据 Jan、Feb 等月份名称判断连续性,因为月份名称无法区分年份。
SQL Server 示例
假设:
custName是客户名称;billing_date是计费日期;billed = 1表示已经计费;- 同一客户在同一个月可能有多条记录。
WITH BillingMonths AS (
SELECT DISTINCT
custName,
DATEFROMPARTS(
YEAR(billing_date),
MONTH(billing_date),
1
) AS billing_month
FROM db_name
WHERE billed = 1
),
CustomerSummary AS (
SELECT
custName,
COUNT(*) AS month_count,
MIN(billing_month) AS first_month,
MAX(billing_month) AS last_month
FROM BillingMonths
GROUP BY custName
)
SELECT
custName,
month_count,
CASE
WHEN DATEDIFF(MONTH, first_month, last_month) + 1 = month_count
THEN 'Concurrent'
ELSE 'Non-Concurrent'
END AS billing_cycle
FROM CustomerSummary
ORDER BY custName;
查询结果如下:
custName month_count billing_cycle
Customer 1234 2 Concurrent
Customer 2345 3 Concurrent
Customer 3456 4 Non-Concurrent
如果需要把结果拼接成完整描述,可以写成:
WITH BillingMonths AS (
SELECT DISTINCT
custName,
DATEFROMPARTS(
YEAR(billing_date),
MONTH(billing_date),
1
) AS billing_month
FROM db_name
WHERE billed = 1
),
CustomerSummary AS (
SELECT
custName,
COUNT(*) AS month_count,
MIN(billing_month) AS first_month,
MAX(billing_month) AS last_month
FROM BillingMonths
GROUP BY custName
)
SELECT
CONCAT(
custName,
' was billed for ',
month_count,
' months ',
CASE
WHEN DATEDIFF(MONTH, first_month, last_month) + 1 = month_count
THEN 'Concurrent'
ELSE 'Non-Concurrent'
END
) AS result
FROM CustomerSummary
ORDER BY custName;
判断逻辑
假设某客户在 1 月、2 月和 3 月都有计费记录:
实际月份数 = 3
最早月份到最晚月份的跨度 = DATEDIFF(MONTH, Jan, Mar) + 1 = 3
两个数值相等,说明这些月份是连续的。
如果计费月份是 2 月、4 月、6 月和 8 月:
实际月份数 = 4
最早月份到最晚月份的跨度 = DATEDIFF(MONTH, Feb, Aug) + 1 = 7
两个数值不相等,说明中间有月份缺失,属于非连续计费。
如果表中分别保存年份和月份
假设年份和月份分别保存在 billing_year 和 billing_month 字段中,其中月份取值为 1 到 12:
WITH BillingMonths AS (
SELECT DISTINCT
custName,
DATEFROMPARTS(
billing_year,
billing_month,
1
) AS billing_month
FROM db_name
WHERE billed = 1
)
SELECT
custName,
COUNT(*) AS month_count,
CASE
WHEN DATEDIFF(
MONTH,
MIN(billing_month),
MAX(billing_month)
) + 1 = COUNT(*)
THEN 'Concurrent'
ELSE 'Non-Concurrent'
END AS billing_cycle
FROM BillingMonths
GROUP BY custName
ORDER BY custName;
MySQL 写法
WITH BillingMonths AS (
SELECT DISTINCT
custName,
DATE_FORMAT(billing_date, '%Y-%m-01') AS billing_month
FROM db_name
WHERE billed = 1
)
SELECT
custName,
COUNT(*) AS month_count,
CASE
WHEN TIMESTAMPDIFF(
MONTH,
MIN(billing_month),
MAX(billing_month)
) + 1 = COUNT(*)
THEN 'Concurrent'
ELSE 'Non-Concurrent'
END AS billing_cycle
FROM BillingMonths
GROUP BY custName
ORDER BY custName;
注意事项
- 需要统计的是不同的计费月份,不是原始记录数,因此要使用
SELECT DISTINCT custName, billing_month。 - 月份信息必须包含年份。
2025-01和2026-01属于两个不同的计费月份。 - 如果
month是数据库关键字或函数名,建议把字段改为billing_month,也可以按照相应数据库的语法进行转义。 - 上述查询用于判断某个客户的全部计费月份能否组成一个连续区间。如果需要分别列出同一客户的多段连续周期,则要使用
LAG()或“日期减行号”的方法进行分段。 Apl通常是Apr的误写。生产数据最好保存为真实日期或数字月份,不要依赖月份的英文缩写。
备注:内容仅供参考。
