使用GROUP BY去重遇ORA-00933错误:SQL命令未正确终止求助
ORA-00933错误排查与去重解决方案
这个错误在Oracle使用GROUP BY的时候太容易踩坑了,我帮你拆解下问题和解决办法:
为什么会触发ORA-00933?
Oracle对GROUP BY的语法要求非常严格:SELECT语句中所有没有使用聚合函数(比如SUM、COUNT)的列,必须全部出现在GROUP BY子句中。另外,也有可能是你把GROUP BY放在了错误的位置(比如ORDER BY之后),或者语法里有多余的符号,导致SQL命令无法正确解析。
从你的描述来看,大概率是只给GROUP BY加了部分列,没有覆盖SELECT里的所有非聚合字段,才触发了这个错误。
两种可行的去重方案
方案1:严格遵循Oracle GROUP BY规则去重
如果你确实需要通过GROUP BY合并完全相同的行(所有字段值都重复的情况),那必须把SELECT里的每一个非聚合列都加到GROUP BY里。另外建议把旧的隐式连接改成显式JOIN,可读性更强也不容易出错:
SELECT TRIM(S90T1.fname) || ' ' || S90T1.lname "_CoWorker", L16T3.partno, L16T3.shortl62, L16T3.partrev, L16T3.ordno, L16T40.reascode || '(' || TRIM(E10T1.ma_desc) || ')' "_reascode", L16T3.datreg "_Date" FROM L16T40 JOIN L16T3 ON L16T3.ordno = L16T40.ordno JOIN S90T1 ON L16T3.logguser = S90T1.uname JOIN E10T1 ON L16T40.reascode = E10T1.reascode WHERE L16T3.L16lcode = 45 AND L16T3.datreg > TO_DATE('2024-01-01', 'YYYY-MM-DD') -- 替换成你的实际日期条件 GROUP BY TRIM(S90T1.fname) || ' ' || S90T1.lname, L16T3.partno, L16T3.shortl62, L16T3.partrev, L16T3.ordno, L16T40.reascode || '(' || TRIM(E10T1.ma_desc) || ')', L16T3.datreg;
小技巧:如果拼接后的列(比如_CoWorker)是由原始字段唯一决定的,你也可以在GROUP BY里用原始字段(比如S90T1.fname, S90T1.lname)代替拼接后的列,效果是一样的,还能减少计算量。
方案2:用窗口函数灵活去重(推荐)
如果你只是想去除_CoWorker的重复,而保留对应行的最新/最早记录(其他字段不需要完全相同),那GROUP BY就不太合适了,这时候用窗口函数ROW_NUMBER()会更灵活:
WITH ranked_data AS ( SELECT TRIM(S90T1.fname) || ' ' || S90T1.lname "_CoWorker", L16T3.partno, L16T3.shortl62, L16T3.partrev, L16T3.ordno, L16T40.reascode || '(' || TRIM(E10T1.ma_desc) || ')' "_reascode", L16T3.datreg "_Date", -- 按_CoWorker分组,每组内按日期倒序排名,最新的行排第1 ROW_NUMBER() OVER (PARTITION BY TRIM(S90T1.fname) || ' ' || S90T1.lname ORDER BY L16T3.datreg DESC) rn FROM L16T40 JOIN L16T3 ON L16T3.ordno = L16T40.ordno JOIN S90T1 ON L16T3.logguser = S90T1.uname JOIN E10T1 ON L16T40.reascode = E10T1.reascode WHERE L16T3.L16lcode = 45 AND L16T3.datreg > TO_DATE('2024-01-01', 'YYYY-MM-DD') -- 替换成你的实际日期条件 ) -- 只取每组排名第一的行 SELECT "_CoWorker", partno, shortl62, partrev, ordno, "_reascode", "_Date" FROM ranked_data WHERE rn = 1;
这个方法可以精准控制去重逻辑,比如你想保留最早的记录,只需要把ORDER BY L16T3.datreg DESC改成ASC就行。
内容的提问来源于stack exchange,提问作者Nixon




