子查询返回多行问题求助:如何计算生产操作时间占比?
解决SQL计算调整后占比(PCT)的问题
Hey there! 看你为这个结果集的问题折腾好久了,我来帮你梳理下思路,应该能解决你的困扰~
首先明确你的核心需求:
- 要展示
ProdOp、OpSUM(每个操作的时长) - 计算
PCT:用总时长减去ProdOp='LL'的时长得到调整后总时长,再用每个OpSUM除以这个调整后总时长,转成百分比(比如你的示例里BB的20分钟占比4.2%)
你提到的子查询返回多行的问题,大概率是因为子查询没做单值聚合——如果子查询返回了多条记录,和主查询关联时就会出错或者得到不符合预期的结果。下面给你两种靠谱的解决方法:
方法1:用标量子查询获取调整后总时长
这种方法适合大多数SQL数据库(MySQL、PostgreSQL、SQL Server都支持),核心是让子查询只返回1行1列的单值:
SELECT ProdOp, OpSUM, -- 计算百分比,先把时间转成数值避免类型错误,保留1位小数 ROUND( -- 把OpSUM转成分钟数(如果是INTERVAL类型) (EXTRACT(EPOCH FROM OpSUM) / 60) / -- 调整后总时长:总时长 - LL的时长,同样转成分钟数 ( (SELECT EXTRACT(EPOCH FROM SUM(TimeSUM))/60 FROM your_table) - (SELECT EXTRACT(EPOCH FROM SUM(TimeSUM))/60 FROM your_table WHERE ProdOp = 'LL') ) * 100, 1 ) AS PCT FROM your_table WHERE ProdOp != 'LL' -- 排除LL本身,和你的示例一致 ORDER BY ProdOp;
关键点:
- 两个子查询都用
SUM(TimeSUM)做聚合,确保只返回一个总时长值,不会出现多行的问题 - 如果你的
TimeSUM是数值类型(比如直接存分钟数),可以去掉EXTRACT(EPOCH ...)的转换,直接用数值计算
方法2:用CTE+交叉连接复用总时长
如果需要多次用到调整后总时长,用CTE(公共表表达式)先计算好,再通过交叉连接把这个全局值和每一行数据关联:
-- 先计算调整后的总时长,存在CTE里 WITH adjusted_total AS ( SELECT (EXTRACT(EPOCH FROM SUM(TimeSUM))/60) - (EXTRACT(EPOCH FROM SUM(CASE WHEN ProdOp = 'LL' THEN TimeSUM ELSE 0 END))/60) AS total_minutes FROM your_table ) SELECT t.ProdOp, t.OpSUM, ROUND( (EXTRACT(EPOCH FROM t.OpSUM)/60) / at.total_minutes * 100, 1 ) AS PCT FROM your_table t -- 交叉连接让每一行都能拿到总时长 CROSS JOIN adjusted_total at WHERE t.ProdOp != 'LL' ORDER BY t.ProdOp;
为什么交叉连接可行?
因为adjusted_total这个CTE只返回1行数据,交叉连接后不会产生笛卡尔积,反而能让每个ProdOp都复用同一个总时长值,完美贴合你之前考虑的交叉连接思路~
排查你之前的问题
如果之前的子查询返回多行,大概率是这两个原因:
- 子查询没加
SUM()聚合,比如直接写SELECT TimeSUM FROM your_table WHERE ProdOp='LL',如果有多个LL的行就会返回多条 - 子查询的过滤条件不对,导致返回了多个分组的结果
按照上面的方法,确保总时长的计算是单值聚合,就不会出现多行的问题啦~
内容的提问来源于stack exchange,提问作者DRUIDRUID
相关产品推荐
相关产品推荐

