Answers:
内联视图(派生表)和Oracle中的WITH子句(CTE)之间有一些重要区别。其中一些是非常通用的,即适用于其他RDBMS。
WITH
可以用于构建递归子查询,内联视图-不(据我所知,所有支持CTE的RDBMS都是一样的) WITH
子句中的子查询更有可能首先在物理上执行;在许多情况下,在WITH
和内联视图之间进行选择会使优化器选择不同的执行计划(我猜这是特定于供应商的,甚至可能是特定于版本的)。WITH
可以将子查询实现为临时表(我不知道是否有其他供应商,但Oracle支持此功能)。WITH
可以多次引用,在其他的子查询和主查询(真大多数RDBMS)。LATERAL
使用派生表,否则无法在同一级别引用先前声明的派生表。
其他答案很好地涵盖了语法差异,因此我不再赘述。取而代之的是,该答案仅涉及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;
考虑以下使用两个派生表的查询:
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;
我们可以查看此查询并快速确定它不会返回任何行。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;
这是计划:
那是一个非常糟糕的计划。Oracle不使用索引,而是将10000 X 10000 = 100000000行具体化到临时表中,最终最终返回0行。该计划的成本约为600万,这比其他查询要高得多。该查询花费了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;
该查询能够使用索引并几乎立即完成。该查询的成本与之前的11相同。因此,对于第二个查询,Oracle使用的启发式方法导致它选择了一个估计成本为6 M的查询,而不是一个估计成本为11的查询。
对于SQL Server,WITH CTE
指定临时命名结果集,但仅对于first才需要CTE
。即
WITH CTE AS (SELECT .... FROM),
CTE2 AS (SELECT .... FROM)
SELECT CTE.Column, CTE2.Column
FROM CTE
INNER JOIN CTE2 on CTE.Column = CTE2.Column
但这不是子查询或相关子查询。使用CTE可以执行某些操作,而使用SQL Server中的子查询则可以完成某些操作,例如更新CTE中引用的表。这是使用CTE更新表的示例。
子查询将类似于
SELECT
C1,
(SELECT C2 FROM SomeTable) as C2
FROM Table
或者,如果要基于a.c1引用/加入/限制结果,则您将在OP中提供相关子查询。
因此,它们绝对不是一回事,尽管在很多情况下,您可以使用这些方法中的一种或多种来获得相同的结果。这仅取决于最终结果是什么。
with
子句和Oracle中的子查询之间的主要区别是,您可以多次引用该子句中的查询。然后,您可以对其进行一些优化,例如使用materialize
提示将其转换为临时表。您也可以通过在with
子句中引用自身来进行递归查询。内联视图无法做到这一点。
MATERIALIZE
响应覆盖优化程序评估。INLINE
相反。
materialize
hint是有效选项。在优化非常复杂的查询时,有时我需要指定它,因为我知道实现CTE将有利于执行计划。
WITH...
)。您可以将每个派生表都重写为CTE,但不能