SQL 在 Oracle HR 模式中

SQL in Oracle HR Schema

我在 Oracle HR 模式中进行了查询以查看以下信息:

  1. 部门所在城市
  2. 部门员工总数

然而,查询无法正确执行并表示这“不是 GROUP BY 表达式”。

有谁知道问题出在哪里?提前致谢。

SELECT department_name, city, COUNT(employees.department_id)
   FROM departments
       JOIN employees on (departments.department_id=employees.department_id)
         JOIN locations USING (location_id)
GROUP BY department_name;

问题是您同时拥有聚合列和 non-aggregated 列(在您的情况下 city 在 select 列表中。

因为我不知道位置 table 的结构并且考虑到 department 只有一个 location 定义你可以使用 max(city),

SELECT department_name, max(city) city, COUNT(employees.department_id) no_of_employees
   FROM departments
       JOIN employees on (departments.department_id=employees.department_id)
         JOIN locations USING (location_id)
GROUP BY department_name;

您正在按部门分组并希望显示部门所在的城市。您希望这会起作用,因为每个部门都在一个城市。 (SQL 人们称之为函数依赖。)

要实现这一点,...

  • 部门名称必须有唯一约束,否则您必须按 department_id 分组
  • DBMS 必须检测并支持聚合查询中的函数依赖性

遗憾的是,Oracle 不支持聚合查询中的函数依赖。它迫使我们将每个这样的列放在 GROUP BY 子句或聚合函数中。

所以要么扩展 GROUP BY 子句:

SELECT d.department_name, l.city, COUNT(e.department_id)
   FROM departments d
       JOIN employees e ON e.department_id = d.department_id
         JOIN locations l USING (location_id)
GROUP BY d.department_name, l.city
ORDER BY d.department_name;

或在该单个值上使用一些聚合函数作为 MINMAX

SELECT d.department_name, MAX(l.city) AS city, COUNT(e.department_id)
   FROM departments d
       JOIN employees e ON e.department_id = d.department_id
         JOIN locations l USING (location_id)
GROUP BY d.department_name
ORDER BY d.department_name;

不过,我更喜欢先聚合,然后再加入。您想加入部门及其员工人数,那么就这样做:

SELECT d.department_name, l.city, COALESCE(e.cnt, 0) AS employee_count
FROM departments d
JOIN locations l USING (location_id)
LEFT JOIN
(
   SELECT department_id, COUNT(*) as cnt
   FROM employees
   GROUP BY department_id
) e ON e.department_id = d.department_id
ORDER BY d.department_name;

正如 Thorsten 出色地解释的那样,您还可以使用 OVERPARTITION BY 函数对数据进行分组,这将消除对 GROUP BY 函数的使用。

SELECT d.department_name, l.city, COUNT(e.department_id) OVER (PARTITION BY e.department_id) as emp_count
   FROM departments d
       JOIN employees e ON e.department_id = d.department_id
         JOIN locations l USING (location_id)
ORDER BY d.department_name;