ORA-00979 不是 Group By 函数错误

ORA-00979 not a Group By function error

我正在尝试 select 表中的 2 个值,员工 emp_name,emp_location 按 emp_location 分组,我知道按功能分组的列需要在 select 子句中,但我想知道是否有任何其他方法可以在单个查询中获取这些值。

我的意图是 select 每个地点仅根据年龄分配一名员工。 示例查询

select emp_name,emp_location 
from Employee 
where emp_age=25 
group by emp_location

请在这方面提供帮助。

非常感谢所有回答这个问题的人。我会尝试学习这些 windows 函数,因为它们非常方便。

ORA-00979 not a Group By function error

SELECT 子句中只允许在 GROUP BY 子句中指定的聚合函数 列。

在这方面,Oracle 严格遵循 SQL 标准。但是,正如您在评论中注意到的那样,在这一点上,其他一些 RDBMS 不如 Oracle 严格。例如,引用 MySQL's documentation(强调我的):

MySQL extends the use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause. [...]

However, this is useful primarily when all values in each nonaggregated column not named in the GROUP BY are the same for each group. The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate.

因此,在推荐的用例中,将额外的列添加到 GROUP BY 子句将导致相同的结果。


select emp_name,emp_location 
--     ^^^^^^^^
--   this is *not* part of the ̀`GROUP BY` clause
from Employee 
where emp_state=25 
group by emp_location

也许你在寻找:

...
group by emp_location, emp_name
select emp_name,emp_location
from Employee
where emp_age=25
group by emp_name,emp_location

select max(emp_name) emp_name,emp_location
from Employee
where emp_age=25
group by emp_location 

这在 MySQL 而在 Oracle 中不起作用的原因是因为在 Oracle 以及大多数其他数据库中,您需要在 group by 中指定一个字段(或表达式)子句,或者它必须是一个聚合,将组中所有值的值组合成一个。例如,这会起作用:

select max(emp_name),emp_location 
from Employee 
where emp_age=25 
group by emp_location

但是,这可能不是最佳解决方案。如果您只想要名称,它会起作用,但是当您想要为员工设置多个字段时,就会遇到麻烦。在那种情况下 max 不会成功。在下面的查询中,您可能会得到与姓氏不匹配的名字。

select max(emp_firstname), max(emp_lastname), emp_location 
from Employee 
where emp_age=25 
group by emp_location 

解决这个问题的方法是使用 window 函数(分析函数)。通过这些,您可以为每条记录生成一个值,而无需立即减少记录数。例如,使用 windowed max 函数,您可以 select 名为 John 的人的最大年龄,并在结果中的每个 John 旁边显示该值,即使如果他们没有那个年龄。

某些函数,如 rankdense_rankrow_number 可用于为每位员工生成一个编号,然后您可以使用该编号进行筛选。在下面的示例中,我为每个位置(分区依据)创建了这样一个计数器,并按名称和 ID 进行排序。您也可以指定其他字段,例如,如果您希望每个年龄每个位置一个名字,您可以在 partition by 中同时指定年龄和位置。如果您想要每个地点最老的员工,您可以删除 where emp_age=25order by emp_age desc

select
  *
from
  (select 
    emp_name, emp_location,
    dense_rank() over (partition by emp_location order by emp_name, emp_id) as emp_rank
  from Employee 
  where emp_age=25)
where
  emp_rank = 1