我们什么时候使用 WITH 子句,它的主要好处是什么?

When do we use WITH clause, and what are main benefits of it?

我正在处理有关优化查询的任务。改进方法之一是使用 WITH 子句。我注意到它做得很好,它导致执行时间更短,但我现在不确定,我应该什么时候使用 WITH 子句,使用它是否有任何风险?

这是我正在处理的查询之一:

WITH MY_TABLE AS 
    (   SELECT PROD_KY,
               sum(GROUPISPRIVATE) AS ISPRIVATE,
               sum(GROUPISSHARED) AS ISSHARED
        FROM
             (
                SELECT GRP_PROD_CUSTOMER.PROD_KY,
                       1 as ISPRIVATE,
                       0 as ISSHARED
                FROM CUSTOMER
                JOIN GRP_CUSTOMER ON GRP_CUSTOMER.CUST_KY = CUSTOMER.CUST_KY
                JOIN GRP_PROD_CUSTOMER ON GRP_PROD_CUSTOMER.GRP_KY = GRP_CUSTOMER.GRP_KY                                                                                                                                                                                                                                                                                                            
                GROUP BY GRP_PROD_CUSTOMER.PROD_KY
             ) 
   GROUP BY PROD_KY
)
SELECT * FROM MY_TABLE;

is there any risk of using it?

是的。 Oracle 可能会决定具体化子查询,这意味着将其结果集写入磁盘然后再读回(除非在 12cR2 或更高版本中可能并不意味着如此)。意外的 I/O 可能会影响性能。并非总是如此,通常我们可以相信优化器会做出正确的选择。但是,Oracle 已经为我们提供了提示,告诉优化器如何处理结果集:/*+ materialize */ 将其具体化,/*+ inline */ 将其保存在内存中。

我从这个潜在的缺点开始,因为我认为了解 WITH 子句不是灵丹妙药很重要,它不会改进每个查询,甚至可能会降低性能。例如,我和其他评论者一样怀疑你发布的查询在任何方面都更快,因为你将它重写为一个常见的 table 表达式。

通常,WITH 子句的用例是:

  1. 我们想多次使用子查询的结果集

    with cte as
      ( select blah from meh )
    select * 
    from t1
         join t2 on t1.id = t2.id
    where t1.col1 in ( select blah from cte )
    and   t2.col2 not in ( select blah from cte)
    
  2. 我们想要构建子查询级联:

    with cte as
      ( select id, blah from meh )
      , cte2 as 
       ( select t2.*, cte.blah
         from cte
              join t2 on t2.id = cte.id)
      , cte3 as 
        ( select t3.*, cte2.*
          from cte2
               join t3 on t3.col2 = cte2.something ) 
       ….
    

第二种方法很有趣,可用于在纯 SQL 中实现复杂的业务逻辑。但它可能会导致程序性思维模式并失去权力集和加入。这也是一种风险。

  1. 我们要使用递归 WITH 子句。这使我们能够用更标准的方法替换 Oracle 自己的 CONNECT BY 语法。 Find out more

  2. 在12c及以后的版本中我们可以在WITH子句中编写用户自定义函数。这是一个强大的功能,特别是对于需要在 PL/SQL 中实现一些逻辑但只有 SELECT 访问数据库的用户。 Find out more

作为记录,我看到了第二种 WITH 子句的一些非常成功和高效的用法。然而,我也看到了使用 WITH 来编写内联视图同样容易。例如,这只是使用 WITH 子句作为语法糖...

with cte as
  ( select id, blah from meh )
select t2.*, cte.blah 
from  t2
      join cte on cte.id = t2.id

... 会更清楚...

select t2.*, cte.blah 
from  t2
      join ( select id, blah from meh ) cte on cte.id = t2.id

您的查询在 WITH 语句(又名 Common Table 表达式,CTE)方面毫无用处

无论如何,使用 WITH 子句有几个好处:

  • 查询可读性更好(在我看来)
  • 您可以在主查询中多次使用同一个子查询。你甚至可以级联它们。
  • Oracle 可以具体化子查询,即 Oracle 可以创建一个临时 table 并将子查询的结果存储在其中。这可以提供更好的性能。

WITH 子句可以作为内联视图处理或作为临时视图解析 table。 SQL WITH 子句与全局临时 table 的使用非常相似。此技术通常用于提高复杂子查询的查询速度,并使 Oracle 优化器能够将必要的谓词推送到视图中。

后者的优点是重复引用子查询可能更有效,因为数据很容易从临时 table 中检索,而不是每次引用都重新查询。您应该根据具体情况评估 WITH 子句的性能影响。

您可以在此处阅读更多内容:

http://www.dba-oracle.com/t_with_clause.htm

https://oracle-base.com/articles/misc/with-clause

WITH 子句在 oracle 中引入以匹配 SQL-99 标准。

主要目的是减少复杂度和重复代码。

假设您需要找到一个部门的平均工资,然后需要获取所有高于该部门平均工资 (d1) 的部门 (d1)。

这可以使对子查询的多个引用更加高效和可读。

MATERIALIZEINLINE 优化器提示可用于影响决策。未记录的 MATERIALIZE 提示告诉优化器将子查询解析为全局临时 table,而 INLINE 提示告诉它内联处理查询。使用提示的决定完全取决于我们将在查询中实现的逻辑。

在oracle 12c中,在WITH子句中引入了PL/SQL块的声明。

必须从oracle文档中引用。

干杯!!

需要考虑的一点是,不同的 RDBMS 处理 with 子句 - 又名通用 table 表达式 (CTE) 又名子查询分解 - 不同:

因此,根据您使用的 RDBMS 及其版本,您的里程可能会有所不同。