按特定客户时段统计准时交付率的SQL查询优化需求
嘿,我来帮你搞定这个查询的修改需求!你的现有查询已经有了基础,但漏掉了几个关键细节——比如服务有效期的判断,还有准时交付率的计算逻辑,我来一步步给你修正:
首先,先看修改后的完整查询:
SELECT i.NumCptCli, i.nomVille, i.heureImp, COUNT(*) AS TotalLivs, ROUND( (SUM(CASE WHEN l.heureLiv <= i.heureImp THEN 1 ELSE 0 END) * 100.0) / COUNT(*), 2 ) AS livsAlheure FROM LIVRAISON l INNER JOIN IMPERATIF i ON l.NumCptClient = i.NumCptCli AND l.nomVille = i.nomVille AND l.DateLiv BETWEEN i.dateDebImp AND i.dateFinImp GROUP BY i.NumCptCli, i.nomVille, i.heureImp;
接下来给你拆解每个修改点的原因:
替换隐式JOIN为显式INNER JOIN:
原来的NATURAL JOIN虽然能匹配同名列,但风险很高(如果后续表加了新的同名列会出问题),而且你之前的需求里明确要求交付日期必须在服务有效期内,这个条件在原查询里完全没加!所以我把JOIN条件明确写出来,加上l.DateLiv BETWEEN i.dateDebImp AND i.dateFinImp,确保只统计属于增值服务范畴的交付记录。计算准时交付率
livsAlheure:
用SUM(CASE WHEN l.heureLiv <= i.heureImp THEN 1 ELSE 0 END)统计所有准时交付的数量,乘以100.0把结果转成百分比格式(用100.0而不是100是为了避免整数除法导致的精度丢失),再除以总交付数COUNT(*),最后用ROUND(..., 2)保留两位小数,和你给出的示例格式一致。如果需要支持一位小数(比如示例里的95.2),可以把ROUND的第二个参数改成1。完善GROUP BY子句:
原查询的GROUP BY只包含了NumCptCli和nomVille,但SELECT里还返回了heureImp,在大多数严格的SQL模式下(比如MySQL的ONLY_FULL_GROUP_BY)会报错,所以必须把heureImp也加入GROUP BY——毕竟每个客户-城镇组合的限时服务时间应该是唯一的,这样做既符合SQL规范,也避免潜在的逻辑问题。自动排除无交付的服务组合:
因为用了INNER JOIN,只有当IMPERATIF里的服务组合在LIVRAISON中有符合条件的交付记录时,才会被纳入结果集,正好满足你“排除无对应交付记录的服务组合”的需求,不需要额外加过滤条件。
如果你的数据库支持窗口函数或者其他聚合语法,也可以用更简洁的写法计算交付率,效果完全一致:
-- 简化版的准时交付率计算 AVG(CASE WHEN l.heureLiv <= i.heureImp THEN 100.0 ELSE 0 END) AS livsAlheure
内容的提问来源于stack exchange,提问作者TigeR

