Ksh*_*rma 10 oracle cte derived-tables
内联视图允许您从子查询中进行选择,就好像它是不同的表一样:
SELECT
*
FROM /* Selecting from a query instead of table */
(
SELECT
c1
FROM
t1
WHERE
c1 > 0
) a
WHERE
a.c1 < 50;
Run Code Online (Sandbox Code Playgroud)
我已经看到这使用不同的术语来引用:内联视图、WITH 子句、CTE 和派生表。对我来说,它们似乎是同一件事的不同供应商特定语法。
这是一个错误的假设吗?这些之间是否有任何技术/性能差异?
a1e*_*x07 10
Oracle 中的内联视图(派生表)和 WITH 子句 (CTE) 之间存在一些重要区别。其中一些非常通用,即适用于其他 RDBMS。
WITH 可用于构建递归子查询,内联视图 - 不(据我所知,所有支持 CTE 的 RDBMS 都是如此) WITH子句中的子查询更有可能首先被物理执行;在许多情况下,在WITH和内联视图之间进行选择会使优化器选择不同的执行计划(我猜这是特定于供应商的,甚至可能是特定于版本的)。WITH可以将子查询具体化为临时表(我不知道是否有任何其他供应商但 Oracle 支持此功能)。WITH可以多次引用,在其他的子查询和主查询(真大多数RDBMS)。Joe*_*ish 10
其他答案很好地涵盖了语法差异,所以我不会深入讨论。相反,这个答案将只涵盖 Oracle 中的性能。
Oracle 优化器可以选择将 CTE 的结果具体化到内部临时表中。它使用启发式方法来执行此操作,而不是基于成本的优化。启发式类似于“如果 CTE 不是一个简单的表达式并且 CTE 在查询中被多次引用,则将其物化”。有一些查询的实现将提高性能。有一些查询的实现会显着降低性能。下面的例子有点做作,但它很好地说明了这一点:
首先创建一个主键包含 1 到 10000 整数的表:
CREATE TABLE N_10000 (NUM_ID INTEGER NOT NULL, PRIMARY KEY (NUM_ID));
INSERT /*+APPEND */ INTO N_10000
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= 10000
ORDER BY LEVEL;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
考虑以下使用两个派生表的查询:
SELECT t1.NUM_ID
FROM
(
SELECT n1.NUM_ID
FROM N_10000 n1
CROSS JOIN N_10000 n2
) t1
LEFT OUTER JOIN
(
SELECT n1.NUM_ID
FROM N_10000 n1
CROSS JOIN N_10000 n2
) t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;
Run Code Online (Sandbox Code Playgroud)
我们可以查看此查询并快速确定它不会返回任何行。Oracle 也应该能够使用索引来确定这一点。在我的机器上,查询几乎立即完成,执行以下计划:
我不喜欢重复自己,所以让我们用 CTE 尝试相同的查询:
WITH N_10000_CTE AS (
SELECT n1.NUM_ID
FROM N_10000 n1
CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;
Run Code Online (Sandbox Code Playgroud)
这是计划:
这真是一个糟糕的计划。Oracle 没有使用索引,而是将 10000 X 10000 = 100000000 行具体化到临时表中,最终只返回 0 行。该计划的成本约为 6 M,远高于其他查询。在我的机器上完成查询需要 68 秒。
请注意,如果临时表空间中没有足够的内存或可用空间,则查询可能会失败。
我可以使用未记录的INLINE提示来禁止优化器实现 CTE:
WITH N_10000_CTE AS (
SELECT /*+ INLINE */ n1.NUM_ID
FROM N_10000 n1
CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;
Run Code Online (Sandbox Code Playgroud)
该查询能够使用索引并几乎立即完成。查询的成本与之前相同,11。因此,对于第二个查询,Oracle 使用的启发式方法导致它选择估计成本为 6 M 的查询,而不是估计成本为 11 的查询。
| 归档时间: |
|
| 查看次数: |
27333 次 |
| 最近记录: |