Subquery Unnesting
Subqueries are nested when they appear in the WHERE clause of the parent statement. When Oracle Database evaluates a statement with a nested subquery, it must evaluate the subquery portion multiple times and may overlook some efficient access paths or joins.
Subquery unnesting unnests and merges the body of the subquery into the body of the statement that contains it, allowing the optimizer to consider them together when evaluating access paths and joins. The optimizer can unnest most subqueries, with some exceptions. Those exceptions include hierarchical subqueries and subqueries that contain a ROWNUM pseudocolumn, one of the set operators, a nested aggregate function, or a correlated reference to a query block that is not the immediate outer query block of the subquery.
Assuming no restrictions exist, the optimizer automatically unnests some (but not all) of the following nested subqueries:
- Uncorrelated IN subqueries
- IN and EXISTS correlated subqueries, as long as they do not contain aggregate functions or a GROUP BY clause
You can enable extended subquery unnesting by instructing the optimizer to unnest additional types of subqueries:
- You can unnest an uncorrelated NOT IN subquery by specifying the HASH_AJ or MERGE_AJ hint in the subquery.
- You can unnest other subqueries by specifying the UNNEST hint in the subquery.
PARAMETER AND HINT
Subquery unnesting did not happen by default in Oracle 8i. In 8i the hidden parameter "_unnest_subquery" defaults to false whereas it defaults to true in 9i, 10G and 11G.
Subquery unnesting is part from query transformations. Query transformation was based on heuristic rules in 9i. From 10G R1 however by default query transformations are cost based since "_optimizer_cost_based_transformation" defaults to linear. ( there are bugs related to cost based query transformation causing huge performance degradation )
We can avoid that subquery unnesting happens at session level using the underscore "_unnest_subquery" parameter
SQL> alter session set "_unnest_subquery"=false;
We can also control subquery nested by UNNEST/NO_UNNEST hint.
We can avoid that subquery unnesting happens at statement level using the hint OPT_PARAM('_unnest_subquery','false')
select /*+ GATHER_PLAN_STATISTICS OPT_PARAM('_unnest_subquery','false') */ * from t_order_or or1 where
cr_fk = 'EUR' and or_totalamount =
(select max(or_totalamount) from t_order_or or2 where or2.cr_fk = or1.cr_fk );
We can avoid that subquery unnesting AND OTHER QUERY TRANSFORMATIONS happens at statement level using the NO_QUERY_TRANSFORMATION hint
select /*+ GATHER_PLAN_STATISTICS NO_QUERY_TRANSFORMATION */ * from t_order_or or1 where
cr_fk = 'EUR' and or_totalamount =
(select max(or_totalamount) from t_order_or or2 where or2.cr_fk = or1.cr_fk );
Terminology
Any sub-query block in a query statement may be called a subquery; however, we use the term subquery for a sub-query block that appears in the WHERE, SELECT and HAVING clauses. Some Oracle documentation uses the term "nested subquery" for what we refer to as a subquery. A sub-query block that appears in the FROM clause is called a view or derived table.
There are many ways to classify a subquery. The main categorization comes from the way a subquery is used in SQL statements. A WHERE clause subquery belongs to one of the following types: SINGLE-ROW, EXISTS, NOT EXISTS, ANY, or ALL. A single-row subquery must return at most one row, whereas the other types of subquery can return zero or more rows. ANY and ALL subqueries are used with relational comparison operators: =, >,>=, <, <=, and <>. In SQL, the set operator IN is used as a shorthand for =ANY and the set operator NOT IN is used as a shorthand for <>ALL.
Query A shows an example of a correlated EXISTS subquery
A.
SELECT C.cust_last_name, C.country_id
FROM customers C
WHERE EXISTS (SELECT 1
FROM sales S
WHERE S.quantity_sold > 1000 and
S.cust_id = C.cust_id);
A column that appears in a subquery is called a correlated column, if it comes from a table not defined by the subquery. The subquery in A is correlated, as it refers to a correlated column, C.cust_id, which comes from, customers, a table not defined by the subquery. The predicate, S.cust_id = C.cust_id, is called a correlating condition or a correlated predicate.
Consider query B, which contains an uncorrelated ANY subquery. Note that queries B and A are semantically equivalent.
B.
SELECT C.cust_last_name, C.country_id
FROM customers C
WHERE C.cust_id =ANY (SELECT S.cust_id
FROM sales S
WHERE S.quantity_sold > 1000);
The subquery in B is uncorrelated, as it does not refer to a correlated column. "C.cust_id = ANY S.cust_id" in B is called a connecting condition.
Subquery Evaluation
A NOT EXISTS subquery evaluates to TRUE, if it returns no rows. The ANY/ALL subquery returns a set of values, and the predicate containing the ANY/ALL subquery will evaluate to TRUE, if it is satisfied. For example, at least one S.cust_id values must match C.cust_id in the connecting condition of the ANY subquery of query B.
Note that in Oracle, a non-unnested ANY and ALL subquery is converted into a correlated EXISTS and NOT EXISTS subquery respectively.
When a correlated subquery is not unnested, the subquery is evaluated multiple times, for each row of the outer tables, substituting the values of correlated columns (e.g., customer.cust_id in A). Thus, table accesses and joins inside the subquery are repeatedly performed with each invocation and join orders involving subquery tables and outer query tables cannot be explored. This type of evaluation also inhibits parallelization.
XA shows the execution plan for query A. Here subquery unnesting has been disabled. Observe that the text of the non-unnested subquery filter is displayed in the predicate dump at the bottom of the plan. As the execution plan shows, the subquery will be evaluated multiple (i.e., 50K) times for each outer row of CUSTOMERS table.
XA.
Execution Plan
----------------------------------------------------------
Plan hash value: 1012411109
----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 18 | 2428K (1)| 08:05:41 | | |
|* 1 | FILTER | | | | | | | |
| 2 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 975K| 405 (1)| 00:00:05 | | |
| 3 | PARTITION RANGE ALL | | 1 | 8 | 54 (0)| 00:00:01 | 1 | 28 |
|* 4 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 1 | 8 | 54 (0)| 00:00:01 | 1 | 28 |
| 5 | BITMAP CONVERSION TO ROWIDS | | | | | | | |
|* 6 | BITMAP INDEX SINGLE VALUE | SALES_CUST_BIX | | | | | 1 | 28 |
----------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "SALES" "S" WHERE "S"."CUST_ID"=:B1 AND
"S"."QUANTITY_SOLD">1000))
4 - filter("S"."QUANTITY_SOLD">1000)
6 - access("S"."CUST_ID"=:B1)
EXISTS and ANY Subqueries
Semi-join is generally used for unnesting EXISTS and ANY subqueries. However, in some cases where duplicate rows are not relevant, inner join can also be used to unnest EXISTS and ANY subqueries. Here we represent semi-join by the following non-standard syntax: T1.x S= T2.y, where T1 is the left table and T2 is the right table of the semi-join. The semantics of semi-join is the following: A row of T1 is returned as soon as T1.x finds a match with any value of T2.y without searching for further matches.
Consider the previously shown query A. Unnesting of the subquery in A produces query C, where the body of the subquery has been merged into the outer query. Here the correlating condition has been turned into a join predicate; customers and sales become the left and right tables respectively in the semi-join.
C.
SELECT C.cust_last_name, C.country_id
FROM customers C, sales S
WHERE S.quantity_sold > 1000 and
C.cust_id S= S.cust_id;
The execution plan of C is shown below as XC. Note the difference between the costs of the plan XC and the plan XA; recall that XA was generated by disabling unnesting. Clearly, the plan with unnesting (XC) is much more optimal; the cost has come down from 2428K to 896. (The query B also produces the same plan as XC.)
XC.
Execution Plan
----------------------------------------------------------
Plan hash value: 3983182601
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 52 | 896 (2)| 00:00:11 | | |
|* 1 | HASH JOIN SEMI | | 2 | 52 | 896 (2)| 00:00:11 | | |
| 2 | PARTITION RANGE ALL| | 1 | 8 | 490 (2)| 00:00:06 | 1 | 28 |
|* 3 | TABLE ACCESS FULL | SALES | 1 | 8 | 490 (2)| 00:00:06 | 1 | 28 |
| 4 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 975K| 405 (1)| 00:00:05 | | |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("S"."CUST_ID"="C"."CUST_ID")
3 - filter("S"."QUANTITY_SOLD">1000)
Now consider query D, which contains an uncorrelated ANY subquery that has two tables.
D.
SELECT C.cust_last_name, C.country_id
FROM customers C
WHERE C.cust_id =ANY (SELECT S.cust_id
FROM sales S, products P
WHERE P.prod_id = S.prod_id and
P.prod_list_price > 105);
The subquery in D can be unnested by using a semi-join; however, the inner join of the tables in the subquery, sales and products must take place before the semi-join is performed. Therefore, an inline view needs to be generated in order to enforce the join order. The query E shows the unnesting transformation of D. Here the subquery is decorrelated and converted into an inline view, which becomes the right table in the semi-join; and the correlated predicate is turned into a join predicate.
E.
SELECT C.cust_last_name, C.country_id
FROM customers C,
(SELECT S.cust_id as s_cust_id
FROM sales S, products P
WHERE P.prod_id = S.prod_id and P.prod_list_price > 105) VW
WHERE C.cust_id S= VW.s_cust_id;
XE shows the execution plan of E. It has an optimizer-generated inline view named VW_SQ_1. Of the three available join methods (i.e., nested-loop, hash, and sort-merge), the hash method was selected by the optimizer to do the semi-join.
XE
Execution Plan
----------------------------------------------------------
Plan hash value: 4030918423
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 7059 | 213K| | 1987 (1)| 00:00:24 | | |
|* 1 | HASH JOIN SEMI | | 7059 | 213K| 1632K| 1987 (1)| 00:00:24 | | |
| 2 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 975K| | 405 (1)| 00:00:05 | | |
| 3 | VIEW | VW_NSO_1 | 849K| 10M| | 493 (2)| 00:00:06 | | |
|* 4 | HASH JOIN | | 849K| 14M| | 493 (2)| 00:00:06 | | |
|* 5 | TABLE ACCESS FULL | PRODUCTS | 67 | 603 | | 3 (0)| 00:00:01 | | |
| 6 | PARTITION RANGE ALL| | 918K| 8075K| | 486 (2)| 00:00:06 | 1 | 28 |
| 7 | TABLE ACCESS FULL | SALES | 918K| 8075K| | 486 (2)| 00:00:06 | 1 | 28 |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("C"."CUST_ID"="CUST_ID")
4 - access("P"."PROD_ID"="S"."PROD_ID")
5 - filter("P"."PROD_LIST_PRICE">105)
NOT EXISTS Subqueries
Anti-join is always used for unnesting NOT EXISTS and ALL subqueries. We represent anti-join by the following non-standard syntax: T1.x A= T2.y, where T1 is the left table and T2 is the right table of the anti-join. The semantics of anti-join is the following: A row of T1 is rejected as soon as T1.x finds a match with any value of T2.y. A row of T1 is returned, only if T1.x does not match with any value of T2.y.
Consider query F, which has a NOT EXISTS subquery containing two tables.
F.
SELECT C.cust_last_name, C.country_id
FROM customers C
WHERE NOT EXISTS (SELECT 1
FROM sales S, products P
WHERE P.prod_id = S.prod_id and
P.prod_min_price > 90 and
S.cust_id = C.cust_id);
The subquery in F can be unnested by using an anti-join; however, the inner join of the tables in the subquery, sales and products must take place before the anti-join is performed. An inline view is generated in order to enforce the join order. This unnesting produces query G; here the inline view becomes the right table of anti-join.
G.
SELECT C.cust_last_name, C.country_id
FROM customers C,
(SELECT S.cust_id AS s_cust_id
FROM sales S, products P
WHERE P.prod_id = S.prod_id and
P.prod_min_price > 90) VW
WHERE C.cust_id A= VW.s_cust_id;
The execution plan of G is shown as XG. Of the three join methods (i.e., nested-loop, hash and sort-merge), the hash method was selected by the optimizer to do the anti-join.
XG.
Execution Plan
----------------------------------------------------------
Plan hash value: 1110507518
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 55500 | 1680K| | 2000 (1)| 00:00:24 | | |
|* 1 | HASH JOIN ANTI | | 55500 | 1680K| 1632K| 2000 (1)| 00:00:24 | | |
| 2 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 975K| | 405 (1)| 00:00:05 | | |
| 3 | VIEW | VW_SQ_1 | 859K| 10M| | 493 (2)| 00:00:06 | | |
|* 4 | HASH JOIN | | 859K| 14M| | 493 (2)| 00:00:06 | | |
|* 5 | TABLE ACCESS FULL | PRODUCTS | 67 | 603 | | 3 (0)| 00:00:01 | | |
| 6 | PARTITION RANGE ALL| | 918K| 8075K| | 486 (2)| 00:00:06 | 1 | 28 |
| 7 | TABLE ACCESS FULL | SALES | 918K| 8075K| | 486 (2)| 00:00:06 | 1 | 28 |
------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("ITEM_1"="C"."CUST_ID")
4 - access("P"."PROD_ID"="S"."PROD_ID")
5 - filter("P"."PROD_MIN_PRICE">90)
Single-Row Aggregated Subqueries
Consider query H that contains an aggregated, correlated, single-row subquery.
H.
SELECT C.cust_last_name, C.cust_income_level
FROM customers C
WHERE C.cust_credit_limit < (SELECT SUM(S.amount_sold)
FROM sales S
WHERE S.cust_id = C.cust_id);
Doing aggregation for multiple values of equality correlation predicate is like doing aggregation and group-by on the local column, and then joining the view with the outer table on the group-by columns. The subquery in H is unnested by decorrelating it and converting it into a group-by view, which is inner joined with the outer table, customer; here both the correlating and connecting conditions have been turned into join predicates. The transformed query is shown as I.
I.
SELECT C.cust_last_name, C.cust_income_level
FROM customers C,
SELECT SUM(S.amount_sold) AS sum_amt, S.cust_id
FROM sales S
GROUP BY S.cust_id) VW
WHERE C.cust_credit_limit < VW.sum_amt and
C.cust_id = VW.cust_id;
XI shows the execution plan of I.
XI.
Execution Plan
----------------------------------------------------------
Plan hash value: 1157114491
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 353 | 22592 | 921 (4)| 00:00:12 | | |
|* 1 | HASH JOIN | | 353 | 22592 | 921 (4)| 00:00:12 | | |
| 2 | VIEW | | 7059 | 179K| 515 (7)| 00:00:07 | | |
| 3 | HASH GROUP BY | | 7059 | 70590 | 515 (7)| 00:00:07 | | |
| 4 | PARTITION RANGE ALL| | 918K| 8973K| 489 (2)| 00:00:06 | 1 | 28 |
| 5 | TABLE ACCESS FULL | SALES | 918K| 8973K| 489 (2)| 00:00:06 | 1 | 28 |
| 6 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 2059K| 405 (1)| 00:00:05 | | |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("C"."CUST_ID"="VW"."CUST_ID")
filter("C"."CUST_CREDIT_LIMIT"<"VW"."SUM_AMT")
Validity of Unnesting
Every subquery, before it can be unnested, goes through a set of validity checks. The optimizer decisions to unnest or not to unnest a subquery can be overridden by specifying an appropriate hint, but the validity requirements cannot be, since unnesting in such cases would not guarantee a semantically equivalent query.
In the following, we enumerate some important checks that currently invalidate subquery unnesting. Note that this list of checks is by no means exhaustive.
- Subqueries that are correlated to non-parent; for example, subquery SQ3 is contained by SQ2 (parent of SQ3) and SQ2 in turn is contained by SQ1 and SQ3 is correlated to tables defined in SQ1.
- A group-by subquery is correlated; in this case, unnesting implies doing join after group-by. Changing the given order of the two operations may not be always legal.
- Connecting or correlating conditions are not well-formed (e.g., they contains a mix of local and correlated columns on either side of the predicate) and the subquery requires inline view generation, as predicates of this kind do not allow separating out view columns and outer table columns.
- For disjunctive subqueries, the outer columns in the connecting or correlating conditions are not the same.
- Using view-merging transformation, Oracle may merge the group-by or distinct inline view generated during unnesting, and therefore the execution plan may not show any view even when a view is expected.
Summary
In these posts we have tried to illustrate the basic ideas behind unnesting of different types of subquery by showing simple example queries. Oracle can handle far more complex queries - query statements with multiple subqueries at one or more levels, multiple tables, correlated and connecting conditions containing inequality predicates and expressions, subqueries that contain set operators, subqueries with group-by and COUNT aggregate function, ALL subqueries containing nullable columns in its connecting condition, and subqueries in disjunction.
If unnesting of a subquery does not require generation of an inline view, then the subquery is always unnested, because this unnesting provides a more efficient execution plan by allowing more join methods and join orders. If the local column (e.g., S.cust_id in A) in the correlating predicate of the subquery has an index on it, then the subquery evaluation becomes akin to doing index-based nested-loop join; and thus, in some cases, not unnesting may be more efficient than unnesting that generates an inline view and enables only sort-merge and hash join of the view with outer tables. Therefore, in Oracle, subquery unnesting which generates inline views, is done based on cost under the cost-based query transformation framework.
参考至:https://blogs.oracle.com/optimizer/entry/optimizer_transformations_subquery_unesting_part_1
https://blogs.oracle.com/optimizer/entry/optimizer_transformations_subquery_unesting_part_2
http://www.online-database.eu/index.php/sql-tuning/177-subquery-unnesting
http://docs.oracle.com/cd/E11882_01/server.112/e41084/queries008.htm#SQLRF52358
本文原创,转载请注明出处、作者
如有错误,欢迎指正
邮箱:czmcj@163.com
相关推荐
步骤1Gradlerepositories { jcenter()}dependencies { implementation ' jp.wasabeef:glide-transformations:4.x.x ' // If you want to use the GPU Filters implementation ' jp.co.cyberagent.android:gpuimage:...
.maven { url " https://jitpack.io " }}} 然后,将该库添加到您的应用程序build.gradle implementation( " com.github.Commit451.coil-transformations:transformations:latest.version.here " ) 对于GPU(本机)...
结合Picasso实现很炫的图片...compile 'com.github.open-android:Picasso-transformations:0.1.0' 3. 使用Picasso加载图片时添加显示效果 Picasso.with(context) .load(url) .transform(new CropCircleTransform
步骤1Gradlerepositories { jcenter()}dependencies { compile ' jp.wasabeef:picasso-transformations:2.x.x ' // If you want to use the GPU Filters compile ' jp.co.cyberagent.android:gpuimage:2.1.0}第2步...
Picasso Transformations ... compile 'jp.wasabeef:picasso-transformations:2.1.2' // If you want to use the GPU Filters compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.
本文档聚焦于在计算机屏幕上生成三维CAD(计算机辅助设计)图像的过程,特别关注视图变换(Viewing Transformations)以及三维模型的投影生成。三维CAD软件在机械工程和工业设计课程中的传播和应用日益普及,但是...
结合Glide实现很炫的图片...compile 'com.github.open-android:Glide-transformations:0.1.0' 3. 使用Picasso加载图片时添加显示效果 Glide.with(context) .load(url) .transform(new CropCircleTransformation())
Android-glide-transformations.zip,一个android转换库,为glide提供各种图像转换。,安卓系统是谷歌在2008年设计和制造的。操作系统主要写在爪哇,C和C 的核心组件。它是在linux内核之上构建的,具有安全性优势。
标题中的“interpretable_transformations”指的是能够理解和解析模型内部工作方式的转换方法。在这个玩具示例中,我们将探讨如何应用可解释的转换到MNIST数据集上的图像,特别是进行旋转操作。MNIST数据集是一个...
在图像处理领域,2D(二维)变换是关键的技术之一,用于改变图像的几何特性,如位置、方向和大小。MATLAB作为一个强大的数值计算和可视化工具,提供了丰富的函数和工具箱来实现这些变换。本教程将重点讨论图像的平移...
安装从下载knockout-transformations.js的副本,并在您的Web应用程序中引用它: <!-- First reference KO itself -->< script src =' knockout-x.y.z.js ' > </ script ><!-- Then reference ...
可用的转换安装只需通过git clone https://github.com/JetBrains-Research/ast-transformations 。作为工具入门运行命令./gradlew :cli -Pinput=<Input directory with python files> -Poutput=<Output> -...
提要提交资料库 提交文件 提交文档位于 。 发展 npm install安装依赖项。 (可能需要几分钟!) npm run dev运行监视服务器。 npm run build来构建,编译,平衡和缩小文件。... 然后,这些已编译的端点将由src手工...
Transformations是一个Python库,用于计算4x4矩阵,以平移,旋转,反射,缩放,剪切,投影,正交化和叠加3D齐次坐标数组,以及在旋转矩阵,欧拉角和四元数之间进行转换。 还包括一个Arcball控制对象,并具有分解变换...
在Transformations.zip文件中,可能包含的资源有原始图像“X”和变换后的图像“Y”,以及可能的变换选项或测试用例。解压文件后,可以加载图像,然后按照上述步骤进行分析和编程,以识别出正确的变换序列。使用...
前言 Glide是 Google推荐的图片加载库,它可以支持来自url,Android资源,文件,Uri中的图片加载,同时还支持gif图片的加载,以及各种图片显示前的bitmap处理(例如:圆角图片,圆形图片,高斯模糊,旋转,灰度等等),缓存处理,...
转变一个帮助几何学生进行描述性和坐标转换的游戏。这个怎么运作本质上,该应用程序会选择一个(或两个)随机几何变换并将其应用于随机生成的多边形。 它显示原图像和图像,然后向用户显示重新创建转换的选项。...
Glide Transformations ... compile 'jp.wasabeef:glide-transformations:2.0.2' // If you want to use the GPU Filters compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.
本项目"matlab-matrix-transformations"专注于提供一个简洁的API,使得用户能够轻松地创建并操作三维空间中的旋转和平移变换矩阵。这些变换矩阵可以表示对象在空间中的运动,包括局部和全局变化。 首先,我们来了解...
cbs2作品集转换 这是一个共享XSLT从pica +到FOLIO xml转换的公共场所 描述:主要的工作是pica2instance.xsl,然后将有辅助样式表(例如,codes2uuid.xsl)映射到初始记录创建后的内容。 这些文件旨在与收割机的转换...