SQL查询每个生产订单首个未完工生产步骤记录
问题原因
你的两种写法报错/结果不正确的原因非常明确:
- 直接分组聚合方案:标准SQL语法规定,
SELECT子句中未被聚合函数包裹的字段,必须全部写入GROUP BY子句。你既没有对MachID做聚合,也没有把它加入分组,数据库无法识别同一OrderID分组下需要取哪条记录的MachID值。即使部分数据库(如旧版MySQL)开启宽松模式允许这类语法不报错,返回的MachID也是分组内随机取值,和你要的最小PosID对应的设备完全不匹配,结果不可信。 - JOIN关联子查询方案:子查询中
MIN("PosID")计算结果没有设置列别名,子查询输出的字段只有OrderID和一个无列名的聚合值,外层关联时自然找不到T1."PosID"这个字段,你尝试写T1.MIN("PosID")、MIN(T1."PosID")都是不符合SQL引用规则的写法,无法生效。
可用实现方案
以下三种写法都能正确返回你需要的结果,可根据你用的数据库版本和数据量选择:
方案1:修正后的JOIN子查询写法(全数据库兼容)
只需要给子查询的最小PosID计算结果加别名即可,兼容性最好,所有支持SQL标准的数据库都能运行:
SELECT T0."OrderID", T0."PosID", T0."MachID" FROM `Table` T0 JOIN ( SELECT "OrderID", MIN("PosID") AS MinPosID FROM `Table` WHERE "Open" = 'Y' GROUP BY "OrderID" ) T1 ON T0."OrderID" = T1."OrderID" AND T0."PosID" = T1.MinPosID WHERE T0."Open" = 'Y'
最后加
T0."Open" = 'Y'条件是为了避免极端情况下同PosID存在已完工记录时匹配到错误数据。
方案2:窗口函数写法(代码最简洁)
如果你用的数据库支持窗口函数(MySQL8.0+、PostgreSQL、SQL Server、Oracle等主流新版本数据库都支持),用ROW_NUMBER()排序取第一条的写法逻辑更直观,性能也更好:
SELECT "OrderID", "PosID", "MachID" FROM ( SELECT "OrderID", "PosID", "MachID", ROW_NUMBER() OVER (PARTITION BY "OrderID" ORDER BY "PosID" ASC) AS step_rn FROM `Table` WHERE "Open" = 'Y' ) t WHERE step_rn = 1
方案3:关联子查询写法(适合小数据量场景)
写法最简短,不需要做表关联,数据量不大时执行效率足够:
SELECT T0."OrderID", T0."PosID", T0."MachID" FROM `Table` T0 WHERE T0."Open" = 'Y' AND T0."PosID" = ( SELECT MIN(T1."PosID") FROM `Table` T1 WHERE T1."Open" = 'Y' AND T1."OrderID" = T0."OrderID" )
结果验证
用你提供的示例数据执行以上任意一种SQL,都会返回符合预期的结果:
| OrderID | PosID | MachID |
|---|---|---|
| 1 | 2 | B |
| 2 | 4 | C |
内容的提问来源于stack exchange,提问作者Daniel Mannion
相关产品推荐
相关产品推荐

