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

Kar*_*ikS 6 oracle having oracle-sqldeveloper having-clause oracle-ords

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

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

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

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

SELECT *
FROM SH.SALES
WHERE amount_sold > 1000
HAVING amount_sold < 2000;
Run Code Online (Sandbox Code Playgroud)

它在 Oracle SQL 开发人员桌面中执行时出错,而在 Oracle SQL 开发人员 Web 中成功运行。

tha*_*ith 10

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

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

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

我们如何做到这一点?

好吧,当您从问题中输入该查询并执行它时,在后端,这实际上并不是执行的内容。

我们用另一个 SELECT 包装该查询,并使用 ROW_NUMBER() OVER 分析函数调用。这允许我们“窗口化”查询结果,在这种情况下,在第 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
Run Code Online (Sandbox Code Playgroud)

好吧,那又怎样?

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

在搜索最佳执行计划之前,优化器始终可以自由地重新排列查询。

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

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
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

请注意 HAVING 已从查询中转换/优化,这让它进入执行阶段。

主要感谢 AskTom 的@connor-mcdonald帮助我解决这个问题。

这就是为什么它可以在 SQL Developer Web 中运行,但不能在 SQL Developer Desktop 中运行,在 SQL Developer Desktop 中,查询完全按照编写的方式执行。

在此处输入图片说明

  • 我在 Oracle AskTom 的 Connor McDonald 的大力帮助下更新了我的答案 (3认同)