인라인 뷰와 WITH 절의 차이점은 무엇입니까?


9

인라인 뷰를 사용하면 마치 다른 테이블 인 것처럼 하위 쿼리에서 선택할 수 있습니다.

SELECT
    *
FROM /* Selecting from a query instead of table */
    (
        SELECT
            c1
        FROM
            t1
        WHERE
            c1 > 0
    ) a
WHERE
    a.c1 < 50;

나는 이것이 인라인 뷰, WITH 절, CTE 및 파생 테이블과 같은 다른 용어를 사용하는 것을 보았습니다. 나에게 그들은 같은 것에 대해 다른 공급 업체 특정 구문 인 것 같습니다.

이것이 잘못된 가정입니까? 이 둘 사이에 기술적 / 성능 차이가 있습니까?


5
표준 SQL의 "공식"이름은 파생 테이블 (Oracle 이름은 Inline View ) 및 공통 테이블 표현식 (= WITH...)입니다. 모든 파생 테이블을 CTE로 다시 작성할 수 있지만 다른 방법으로는 불가능합니다 (예 : 재귀 CTE 또는 CTE를 여러 번 사용)
dnoeth

답변:


8

Oracle의 인라인 뷰 (유도 된 테이블)와 WITH 절 (CTE) 사이에는 몇 가지 중요한 차이점이 있습니다. 그들 중 일부는 매우 보편적입니다. 즉 다른 RDBMS에 적용 할 수 있습니다.

  1. WITH 인라인 뷰가 아닌 재귀 서브 쿼리를 작성하는 데 사용할 수 있습니다 (CTE를 지원하는 모든 RDBMS에 대해 동일하다는 것을 알고있는 한)
  2. 하위 쿼리 in WITH절이 물리적으로 먼저 실행될 가능성이 높습니다. 대부분의 경우 WITH인라인보기와 인라인보기를 선택하면 옵티마이 저가 다른 실행 계획을 선택할 수 있습니다 (공급 업체별, 버전 별일 수도 있음).
  3. 하위 쿼리 WITH는 임시 테이블로 구체화 될 수 있습니다 (Oracle 이외의 다른 공급 업체 가이 기능을 지원하는지 여부는 알 수 없습니다).
  4. 서브 쿼리는 in WITH, 다른 서브 쿼리 및 기본 쿼리에서 여러 번 참조 될 수 있습니다 (대부분의 RDBMS의 경우 true).

MySQL (최소한 MariaDB 버전)은 파생 테이블을 구체화하고 인덱스를 추가 할 수 있습니다.
ypercubeᵀᴹ

3
CTE를 사용하는 것이 일반적으로 사람에게 더 읽기 쉽습니다.
Joishi Bodio 17

@JoishiBodio : 개인적으로, 나는 당신에 동의하지만, 가독성은 상당히 주관적인 문제입니다. 차라리 언급하고
싶지

또한 CTE는 이전에 선언 된 CTE를 참조 할 수 있습니다. 파생 테이블은 사용하지 않는 한 이전에 선언 된 파생 테이블을 동일한 수준에서 참조 할 수 없습니다 LATERAL.
Lennart

8

다른 답변은 구문 차이를 잘 다루므로 이에 들어 가지 않습니다. 대신이 답변은 Oracle의 성능 만 다룹니다.

Oracle Optimizer는 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;

이 쿼리를보고 어떤 행도 반환하지 않는지 빠르게 확인할 수 있습니다. 오라클은 인덱스를 사용하여이를 확인할 수 있어야합니다. 내 컴퓨터에서 쿼리는 다음 계획으로 거의 즉시 완료됩니다.

좋은 계획

반복하는 것을 좋아하지 않으므로 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 개의 행을 반환합니다. 이 계획의 비용은 약 6M이며 다른 쿼리보다 훨씬 높습니다. 컴퓨터에서 쿼리를 마치는 데 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에서 사용하는 휴리스틱을 통해 예상 비용이 11 인 쿼리 대신 6M의 예상 비용으로 쿼리를 선택했습니다.


1

SQL Server의 WITH CTE경우 이름이 지정된 임시 결과 집합을 지정하지만 첫 번째에만 필요합니다 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에서 제공 한 것입니다.

따라서 그것들은 확실히 같은 것은 아니지만, 많은 경우에 동일한 결과를 얻기 위해 이러한 방법 중 하나 이상을 사용할 수 있습니다. 그것은 그 최종 결과가 무엇인지에 달려 있습니다.


1

withOracle에서 절과 하위 쿼리 의 주요 차이점은 절 내에서 쿼리를 여러 번 참조 할 수 있다는 것입니다. 그런 다음 materialize힌트를 사용하여 임시 테이블로 바꾸는 것처럼 최적화를 수행 할 수 있습니다 . with절 안에서 자체를 참조하여 재귀 쿼리를 수행 할 수도 있습니다 . 인라인 뷰로는 그렇게 할 수 없습니다.

자세한 내용은 여기여기를 참조하십시오 .


일반적으로 구체화 힌트는 필요하지 않습니다. 기본적으로 Oracle 옵티마이 저는 CTE를 구체화하는 것이 타당한 지 여부를 결정하지만 힌트 MATERIALIZEresp로 옵티 마이저 평가를 덮어 쓸 수 있습니다 . INLINE반대로.
Wernfried Domscheit

@WernfriedDomscheit 그건 사실입니다. 그러나 때때로 옵티마이 저는 CTE를 구체화하지 않기 때문에 materialize힌트를 사용하는 것이 유효한 옵션입니다. CTE를 구체화하는 것이 실행 계획에 도움이된다는 것을 알고 매우 복잡한 쿼리를 최적화 할 때 때때로이를 지정해야했습니다.
Marko Vodopija

0

오라클뿐만 아니라 SQL 서버의 CTE에주의해야합니다 .CTE를 하위 쿼리, 크로스 적용 등과 비교할 때 쿼리 성능이 훨씬 저하되는 경우가 있습니다.

항상 그렇듯이 다양한로드 조건에서 쿼리를 테스트하여 가장 적합한 쿼리를 결정하는 것이 중요합니다.

oracle을 사용하는 @scsimon과 유사하게 때때로 MS SQL Server가 인덱스 사용과 관련하여 예상 한 작업을 수행하지 않습니다.

동일한 데이터를 두 번 이상 사용하려는 경우 CTE가 더 유용 할 수 있습니다. 한 번만 사용하는 경우 하위 쿼리가 큰 데이터 세트에서 더 빠릅니다.

예를 들어 select * from (내 하위 쿼리) 다른 것을 조인하십시오 ...

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.