我们什么时候使用 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 子句的用例是:
我们想多次使用子查询的结果集
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)
我们想要构建子查询级联:
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 中实现复杂的业务逻辑。但它可能会导致程序性思维模式并失去权力集和加入。这也是一种风险。
我们要使用递归 WITH 子句。这使我们能够用更标准的方法替换 Oracle 自己的 CONNECT BY 语法。 Find out more
在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 子句的性能影响。
您可以在此处阅读更多内容:
WITH
子句在 oracle 中引入以匹配 SQL-99 标准。
主要目的是减少复杂度和重复代码。
假设您需要找到一个部门的平均工资,然后需要获取所有高于该部门平均工资 (d1) 的部门 (d1)。
这可以使对子查询的多个引用更加高效和可读。
MATERIALIZE
和 INLINE
优化器提示可用于影响决策。未记录的 MATERIALIZE 提示告诉优化器将子查询解析为全局临时 table,而 INLINE
提示告诉它内联处理查询。使用提示的决定完全取决于我们将在查询中实现的逻辑。
在oracle 12c中,在WITH
子句中引入了PL/SQL块的声明。
必须从oracle文档中引用。
干杯!!
需要考虑的一点是,不同的 RDBMS 处理 with 子句 - 又名通用 table 表达式 (CTE) 又名子查询分解 - 不同:
- Oracle 可能会使用具体化或内联(如 APC 提供的答案中所述)
- postgres 总是在 11 之前的版本中使用物化(所以这里的 CTE 是一个优化栅栏)。在 postgres 12 中,行为发生了变化,类似于 Oracle 的方法:https://info.crunchydata.com/blog/with-queries-present-future-common-table-expressions。你甚至有一些看起来像提示的东西(虽然众所周知 postgres 不使用提示......)
- in SQL 当前 CTE 的服务器始终是内联的,如 https://erikdarlingdata.com/2019/08/what-would-materialized-ctes-look-like-in-sql-server/
中所述
因此,根据您使用的 RDBMS 及其版本,您的里程可能会有所不同。
我正在处理有关优化查询的任务。改进方法之一是使用 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 子句的用例是:
我们想多次使用子查询的结果集
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)
我们想要构建子查询级联:
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 中实现复杂的业务逻辑。但它可能会导致程序性思维模式并失去权力集和加入。这也是一种风险。
我们要使用递归 WITH 子句。这使我们能够用更标准的方法替换 Oracle 自己的 CONNECT BY 语法。 Find out more
在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 子句的性能影响。
您可以在此处阅读更多内容:
WITH
子句在 oracle 中引入以匹配 SQL-99 标准。
主要目的是减少复杂度和重复代码。
假设您需要找到一个部门的平均工资,然后需要获取所有高于该部门平均工资 (d1) 的部门 (d1)。
这可以使对子查询的多个引用更加高效和可读。
MATERIALIZE
和 INLINE
优化器提示可用于影响决策。未记录的 MATERIALIZE 提示告诉优化器将子查询解析为全局临时 table,而 INLINE
提示告诉它内联处理查询。使用提示的决定完全取决于我们将在查询中实现的逻辑。
在oracle 12c中,在WITH
子句中引入了PL/SQL块的声明。
必须从oracle文档中引用。
干杯!!
需要考虑的一点是,不同的 RDBMS 处理 with 子句 - 又名通用 table 表达式 (CTE) 又名子查询分解 - 不同:
- Oracle 可能会使用具体化或内联(如 APC 提供的答案中所述)
- postgres 总是在 11 之前的版本中使用物化(所以这里的 CTE 是一个优化栅栏)。在 postgres 12 中,行为发生了变化,类似于 Oracle 的方法:https://info.crunchydata.com/blog/with-queries-present-future-common-table-expressions。你甚至有一些看起来像提示的东西(虽然众所周知 postgres 不使用提示......)
- in SQL 当前 CTE 的服务器始终是内联的,如 https://erikdarlingdata.com/2019/08/what-would-materialized-ctes-look-like-in-sql-server/ 中所述
因此,根据您使用的 RDBMS 及其版本,您的里程可能会有所不同。