使用开发人员桌面和开发人员网络在 Oracle 数据库中没有 GROUP BY 的 HAVING 子句

HAVING clause without GROUP BY in Oracle database using developer desktop and developer web

根据标准做法,我的理解是 HAVING 将与 GROUP BY 一起用于过滤条件,而 WHERE 应该用于一般的按行过滤条件。

然而,关于是否使用 HAVING 作为 WHERE 子句的超集,online discussions 的结论不一。也就是说,它是否可以在没有 GROUP BY 的情况下使用,在这种情况下它可以用作 WHERE 子句。

我想了解在 Oracle、Microsoft SQL 服务器、MySQL、PostGreSQL 和其他工具中使用 HAVING 子句的行业惯例。

我在执行此查询时观察到的一件有趣的事情:

SELECT *
FROM SH.SALES
WHERE amount_sold > 1000
HAVING amount_sold < 2000;

在 Oracle SQL developer desktop 中执行时出错,而在 Oracle SQL developer web 中运行成功。

这是一个很好的问题和难题!

Oracle SQL Developer Web 是通过 Oracle REST 数据服务 (ORDS) 提供的。有一个 RESTful Web 服务用于执行 'ad hoc' SQL 语句和脚本。

我们不是在一次调用中从查询中取回所有行,而是对它们进行分页。我们坚持使用 RESTful 方式,并在一次调用和响应中完成所有工作,而不是保持结果集打开并处理 运行ning。

我们如何做到这一点?

好吧,当您输入问题中的查询并执行它时,在后端,实际上并没有执行什么。

我们用另一个 SELECT 包装该查询,并使用 ROW_NUMBER() OVER 分析函数调用。这允许我们 'window' 查询结果,在本例中为第 1 行和第 26 行之间,或者该查询的前 25 行,您的查询。

SELECT *
  FROM (
       SELECT Q_.*,
              ROW_NUMBER() OVER(
                      ORDER BY 1
              ) RN___
         FROM (
              select * 
from sh.sales
where amount_sold > 1000
having amount_sold < 2000
       ) Q_
)
 WHERE RN___ BETWEEN :1 AND :2

好吧,那又怎样?

好吧,优化器发现这个查询仍然可以 运行,即使 having 子句不合适。

优化器总是可以在搜索最佳执行计划之前自由地重新安排查询。

在这种情况下,10053 跟踪显示来自 SQL Dev Web 的如下查询(我正在使用 EMP,但同样适用于任何 table)

SELECT *
  FROM (
       SELECT Q_.*,
              ROW_NUMBER() OVER(
                      ORDER BY 1
              ) RN___
         FROM (
              SELECT *
              FROM emp
              WHERE sal > 1000
HAVING sal < 2000
       ) Q_
)
 WHERE RN___ BETWEEN :1 AND :2

在针对计划进行优化之前在内部转换为以下内容。

SELECT 
  subq.EMPNO EMPNO,
  subq.ENAME ENAME,
  subq.JOB JOB,
  subq.MGR MGR,
  subq.HIREDATE HIREDATE,
  subq.SAL SAL,subq.COMM COMM,
  subq.DEPTNO DEPTNO,
  subq.RN___ RN___ 
FROM  
  (SELECT 
      EMP.EMPNO EMPNO,
      EMP.ENAME ENAME,
      EMP.JOB JOB,EMP.MGR MGR,
      EMP.HIREDATE HIREDATE,
      EMP.SAL SAL,
      EMP.COMM COMM,
      EMP.DEPTNO DEPTNO,
      ROW_NUMBER() OVER ( ORDER BY  NULL ) RN___ 
   FROM EMP EMP 
   WHERE EMP.SAL>1000 AND TO_NUMBER(:B1)>=TO_NUMBER(:B2)
   ) subq 
WHERE subq.RN___>=TO_NUMBER(:B3) 
AND subq.RN___<=TO_NUMBER(:B4)

请注意 HAVING 已 transformed/optimized 在查询之外,这让它可以进入执行阶段。

AskTom 帮助我解析这个问题 @connor-mcdonald 的主要成就。

这就是为什么它在 SQL Developer Web 中有效,但在 SQL Developer Desktop 中无效,因为在 Developer Desktop 中查询完全按照编写的执行。