在这种情况下,为什么我需要创建一个子查询而不是仅仅进行比较?

Why do I need to create a subquery instead of just comparing in this case?

我写了这个查询

SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME
FROM employees
WHERE SALARY > (SELECT AVG(SALARY)
                FROM employees)

我有点困惑为什么我必须为它创建一个子查询,为什么我不能像这样写查询:

SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME
FROM employees
WHERE SALARY > AVG(SALARY)

SQL 是一种基于集合的语言。如果您只聚合一组中的一列,则必须聚合整个集合。您不能既聚合一个集合又不聚合一个集合。此外,SELECT 语句仅定义一个集合。因此,您无法在同一 SELECT 语句中将聚合集中的值与 non-aggregated 集(两组)中的值进行比较。

您的第一个语句有效,因为您有两个 SELECT 语句,每个语句都定义了一个不同的集合。一组通过 avg() 函数聚合,另一组保持 non-aggregated.

它也有效,因为聚合集是标量的(它只有一个值)。它可以将 non-aggregated 集中的每一行与 single-value 聚合集中保存的标量值进行比较。如果以非标量的方式定义聚合集,则会抛出错误,您将不得不通过 [=17] 中的 ON 子句在两个集合之间建立关系=] 在您的 FROM 子句中或通过相关的子查询。

另一个原因是操作顺序。通过 WHERE 子句的过滤首先发生在 SQL 的执行中。在过滤您的数据时,尚未发生其他操作。 GROUP BY/Aggregation 在 SQL 的执行中出现得很晚。因此,您试图将 SQL 中两个截然不同的步骤的结果相互比较。

操作顺序是HAVING存在的原因。它与 WHERE 非常相似,但在聚合之后起作用。不过,这对您在此 SQL 中尝试做的事情没有帮助,因为您再次尝试将 non-aggregated 集合中的值与聚合集合中的值进行比较不能在单个 SELECT 语句中完成。


可能值得注意的是,您可以使用“window 函数”(也称为“有序分析函数”或“分析函数”)在 non-aggregated 集合内聚合 (so-to-speak) ") 大多数 RDBMS 都支持。

例如:

SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME, SALARY, AVG(SALARY) OVER () as avg_salary
FROM employees;

这仍然会为 table employees 中的每一行吐出一个 non-aggregated 行。它将有第 4 列,其中每一行包含所有员工的平均工资。 每一行都包含相同的值。

+-------------+------------+------------+--------+------------+
| EMPLOYEE_ID | FIRST_NAME | LAST_NAME  | SALARY | avg_salary |
+-------------+------------+------------+--------+------------+
|           1 | bob        | mcbob      |    100 |       210  |
|           2 | sue        | o'susan    |    230 |       210  |
|           3 | venkat     | van venkat |    300 |       210  |
+-------------+------------+------------+--------+------------+
   

也就是说,由于操作顺序的原因,您不能比较 WHEREHAVING 子句中 window 函数的结果。 Window 函数逻辑几乎在 sql 执行的每一步之后运行。您只会得到一个不允许的错误。因此,您将再次需要两个 SELECT 语句:

SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME
FROM 
    (
         SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME, SALARY, AVG(SALARY) OVER () as avg_salary
         FROM employees;
    ) dt
WHERE SALARY > avg_salary;

最后,市场上有两个具有 QUALIFY 子句的 RDBMS(Snowflake 和 Teradata)类似于 WHEREHAVING 子句,它们允许 window 函数成为过滤器的一部分。如果您使用的是这两个平台之一,那么您可以将其写为单个 SELECT 语句:

SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME
FROM employees
QUALIFY SALARY > AVG(SALARY) OVER (); 

就像 WHERE 在执行开始时起作用,而 HAVING 在接近执行结束时起作用,QUALIFY 甚至比这更晚(就在 [=31= 之前) ]).奇怪的是,这正是您最初想要的,并且某些 RDBMS 预料到了您的需求。我希望更多的 RDBMS 在未来的版本中采用 QUALIFY 子句,因为它非常方便。