현재 선택한 SQL 과정

SQLP 이론 학습

이론 목록으로 돌아가기

LATERAL·APPLY 조인: 왼쪽 상관 참조와 Key별 Top-N

왼쪽 Row를 참조하는 인라인 집합을 조인하는 APPLY 문법과 Outer 보존 의미를 예제로 익힙니다.

예상 읽기 20

핵심 요약

LATERAL, CROSS APPLY, OUTER APPLY의 공통 핵심은 FROM 절의 오른쪽 Row Source가 왼쪽 Row Source의 Column을 참조할 수 있게 하는 왼쪽 상관(Left Correlation) 입니다.

다만 역할은 구분해야 합니다.

  • LATERAL: 오른쪽 Inline View에 왼쪽 Column을 참조할 수 있는 상관 범위를 부여합니다.
  • CROSS APPLY: 왼쪽 상관을 허용하면서, 오른쪽 결과가 있는 왼쪽 행만 반환합니다.
  • OUTER APPLY: 왼쪽 상관을 허용하면서, 오른쪽 결과가 없어도 왼쪽 행을 보존합니다.
CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
왼쪽 Row 한 건
→ 왼쪽 값을 오른쪽 Query Block에서 참조
→ 오른쪽에서 0건·1건·여러 건 생성
→ APPLY 종류 또는 결합 방식에 따라 왼쪽 Row 보존 여부 결정
문법오른쪽 결과 0건오른쪽 결과 1건오른쪽 결과 N건
CROSS APPLY왼쪽 행 제거1행왼쪽 행이 N행으로 증가
OUTER APPLY왼쪽 행 1행 보존, 오른쪽은 NULL1행왼쪽 행이 N행으로 증가
LATERALLATERAL 자체가 아니라 함께 사용한 Join 방식이 결정1행오른쪽 행 수만큼 결합

APPLY 문법을 사용했다고 해서 물리적으로 반드시 “왼쪽 행마다 오른쪽을 한 번씩 실행”하는 Plan이 고정되는 것은 아닙니다. Optimizer는 View Merging, Predicate Pushing, Join 순서 변경 등 비용 기반 변환을 적용할 수 있으므로 실제 동작은 실행계획과 Runtime 통계로 검증해야 합니다.

학습 목표

이 이론을 학습한 뒤에는 다음을 설명할 수 있어야 합니다.

  1. 일반 Inline View와 Lateral Inline View의 상관 범위 차이를 설명한다.
  2. CROSS APPLYOUTER APPLY의 왼쪽 행 보존 규칙을 계산한다.
  3. 오른쪽 결과가 0·1·N건일 때 최종 결과 행 수를 예측한다.
  4. Key별 Top-N에서 결정적인 정렬과 Tie-Breaker가 필요한 이유를 설명한다.
  5. 상관 Key와 정렬 Key에 맞춘 Index가 Stopkey 처리에 유리한 이유를 설명한다.
  6. APPLY 내부의 GROUP BY 없는 집계가 입력 0건에서도 한 행을 반환하는 이유를 설명한다.
  7. APPLY와 분석 함수 방식의 비용 구조를 비교한다.
  8. Starts, A-Rows, Buffers를 이용해 1회당 반복 작업량을 계산한다.

1. 핵심 용어: Query Block, Inline View, 왼쪽 상관

1.1 Query Block과 Inline View

SELECT 문 안의 독립적인 SELECT 단위를 Query Block이라고 합니다. FROM 절에 배치한 Subquery는 Inline View입니다.

일반 Inline View는 같은 FROM 절에서 자신보다 왼쪽에 있는 Table Alias를 임의로 참조할 수 없습니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT c.customer_id,
       x.order_id
FROM   customers c
JOIN   (
         SELECT o.order_id
         FROM   orders o
         WHERE  o.customer_id = c.customer_id
       ) x
  ON   1 = 1;

위 문장에서 x는 일반 Inline View이므로 c.customer_id를 참조할 상관 범위가 없습니다.

1.2 LATERAL이 제공하는 왼쪽 상관

LATERAL을 지정하면 오른쪽 Subquery 안에서 FROM 절 왼쪽에 먼저 정의된 Table의 Column을 참조할 수 있습니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT c.customer_id,
       x.order_id
FROM   customers c,
       LATERAL (
         SELECT o.order_id
         FROM   orders o
         WHERE  o.customer_id = c.customer_id
       ) x;

같은 상관 관계를 CROSS APPLY로 표현할 수 있습니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT c.customer_id,
       x.order_id
FROM   customers c
CROSS APPLY (
    SELECT o.order_id
    FROM   orders o
    WHERE  o.customer_id = c.customer_id
) x;

여기서 중요한 점은 SQL을 절차형 반복문으로 강제한다는 의미가 아니라, 오른쪽 Query Block이 왼쪽 Column을 사용할 수 있는 논리적 범위를 만든다는 것입니다. 물리 실행 방식은 Optimizer가 비용을 기준으로 결정합니다.

1.3 LATERAL의 대표 제약

Oracle AI Database 26ai 기준으로 Lateral Inline View에는 다음과 같은 제약이 있습니다.

  • 같은 Table Reference에서 PIVOT, UNPIVOT, Pattern을 함께 지정할 수 없습니다.
  • Right Outer Join 또는 Full Outer Join의 첫 번째 Table을 향한 왼쪽 상관에는 제약이 있습니다.
  • query_partition_clause와 Join 위치가 결합될 때 왼쪽 상관 범위에 추가 제약이 생길 수 있습니다.

시험과 실무에서는 모든 제약을 암기하기보다, LATERAL왼쪽 상관을 허용하는 문법이라는 핵심과 Join 방향에 따른 제한이 있다는 점을 우선 이해합니다.


2. CROSS APPLY와 OUTER APPLY의 결과 규칙

다음 데이터를 사용합니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
CUSTOMERS
CUSTOMER_ID | NAME
------------+-----
10          | A
20          | B
30          | C

ORDERS
ORDER_ID | CUSTOMER_ID | AMOUNT
---------+-------------+-------
101      | 10          | 5000
102      | 10          | 7000
201      | 20          | 3000

2.1 CROSS APPLY

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT c.customer_id,
       x.order_id
FROM   customers c
CROSS APPLY (
    SELECT o.order_id
    FROM   orders o
    WHERE  o.customer_id = c.customer_id
) x
ORDER BY c.customer_id, x.order_id;
CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
10 | 101
10 | 102
20 | 201

고객 30은 오른쪽 결과가 0건이므로 제거됩니다. 고객 10은 오른쪽 결과가 2건이므로 두 행으로 증가합니다.

2.2 OUTER APPLY

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT c.customer_id,
       x.order_id
FROM   customers c
OUTER APPLY (
    SELECT o.order_id
    FROM   orders o
    WHERE  o.customer_id = c.customer_id
) x
ORDER BY c.customer_id, x.order_id;
CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
10 | 101
10 | 102
20 | 201
30 | NULL

고객 30도 보존되며 오른쪽 출력 Column은 NULL로 확장됩니다.

2.3 최종 행 수 계산

왼쪽 i번째 행에 대해 오른쪽 결과 건수를 mᵢ라고 하면 다음처럼 계산할 수 있습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
CROSS APPLY 기여 행 수 = mᵢ
OUTER APPLY 기여 행 수 = max(1, mᵢ)

예를 들어 오른쪽 결과 건수가 차례로 2, 0, 1이면 다음과 같습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
CROSS APPLY 전체 행 수 = 2 + 0 + 1 = 3
OUTER APPLY 전체 행 수 = 2 + 1 + 1 = 4

OUTER APPLY가 오른쪽에서 항상 한 행만 반환하는 것은 아닙니다. 오른쪽에서 N행이 나오면 해당 왼쪽 행도 N행으로 증가합니다.


3. LATERAL과 APPLY를 구분하는 기준

구분핵심 역할미매칭 왼쪽 행 보존
LATERAL오른쪽 Inline View에 왼쪽 상관 범위 부여함께 사용한 Join 방식이 결정
CROSS APPLY왼쪽 상관 + Inner/Cross 계열 결합 의미보존하지 않음
OUTER APPLY왼쪽 상관 + Left Outer 계열 결합 의미보존함

따라서 다음 문장은 정확하지 않습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
LATERAL은 미매칭 왼쪽 행을 보존한다.

정확한 표현은 다음과 같습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
LATERAL은 상관 범위를 제공하고,
미매칭 행 보존 여부는 함께 사용한 Join 방식이 결정한다.

4. Key별 최근 주문 한 건 조회

고객마다 가장 최근 주문 한 건을 조회합니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT c.customer_id,
       c.customer_name,
       x.order_id,
       x.order_date,
       x.amount
FROM   customers c
OUTER APPLY (
    SELECT o.order_id,
           o.order_date,
           o.amount
    FROM   orders o
    WHERE  o.customer_id = c.customer_id
    ORDER BY o.order_date DESC,
             o.order_id   DESC
    FETCH FIRST 1 ROW ONLY
) x;

결과 한 행의 의미는 다음과 같습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
고객 한 명 + 그 고객의 결정적인 최신 주문 최대 한 건

4.1 Tie-Breaker가 필요한 이유

ORDER_DATE가 같은 주문이 여러 건이면 날짜만으로는 첫 번째 행이 결정되지 않습니다. FETCH FIRST 1 ROW ONLY를 사용하더라도 정렬 Key가 유일하지 않으면 실행 시점과 접근 경로에 따라 선택 행이 달라질 수 있습니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
ORDER BY o.order_date DESC,
         o.order_id   DESC

ORDER_ID처럼 유일한 Column을 마지막 Tie-Breaker로 추가하면 결과가 결정적입니다.

4.2 CROSS APPLY와 OUTER APPLY 선택

  • 주문이 없는 고객도 보여야 함: OUTER APPLY
  • 최근 주문이 있는 고객만 필요함: CROSS APPLY

문법 선택은 성능보다 먼저 결과 의미로 결정해야 합니다.


5. Key별 Top-N과 Index 설계

다음 Index를 가정합니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
CREATE INDEX orders_cust_dt_ix
    ON orders(customer_id, order_date DESC, order_id DESC);

고객 한 건이 주어졌을 때 기대하는 접근 순서는 다음과 같습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
CUSTOMER_ID 등치 탐색
→ 해당 고객 범위에서 ORDER_DATE 최신 방향으로 읽기
→ ORDER_ID로 동점 순서 확정
→ 첫 행을 찾으면 중단

가능한 Plan 개념 구조는 다음과 같습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
NESTED LOOPS OUTER
  TABLE ACCESS ... CUSTOMERS
  VIEW
    WINDOW NOSORT STOPKEY
      TABLE ACCESS BY INDEX ROWID ORDERS
        INDEX RANGE SCAN ORDERS_CUST_DT_IX

Operation 이름은 Version, 통계, Transformation, Select List에 따라 달라질 수 있습니다. 판단의 핵심은 다음입니다.

  1. CUSTOMER_ID가 Index access 조건으로 사용되는가?
  2. 정렬 Key가 Index 순서와 맞아 별도 Sort가 줄어드는가?
  3. Top-N 행 제한이 STOPKEY 계열 Operation으로 나타나는가?
  4. 필요한 Column이 Index에 없으면 Table Access 비용이 얼마나 추가되는가?

AMOUNT까지 자주 조회하고 Table Access가 병목이라면 (customer_id, order_date DESC, order_id DESC, amount) 같은 확장 Index를 검토할 수 있습니다. 그러나 Index 폭과 DML 비용이 증가하므로 무조건 추가하지 않고 실제 I/O와 업무 빈도로 판단합니다.


6. APPLY와 분석 함수 방식 비교

모든 고객의 최신 주문을 한 번에 계산하는 방식입니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT c.customer_id,
       c.customer_name,
       r.order_id,
       r.order_date,
       r.amount
FROM   customers c
LEFT JOIN (
    SELECT o.order_id,
           o.customer_id,
           o.order_date,
           o.amount,
           ROW_NUMBER() OVER (
               PARTITION BY o.customer_id
               ORDER BY o.order_date DESC,
                        o.order_id   DESC
           ) AS rn
    FROM   orders o
) r
  ON r.customer_id = c.customer_id
 AND r.rn = 1;
판단 조건APPLY Top-N분석 함수 방식
대상 고객 수적고 선택적이면 유리할 수 있음대부분의 고객을 처리하면 유리할 수 있음
주문 접근고객별 반복 Probe주문 집합을 넓게 처리
정렬Index 순서로 생략 가능성WINDOW SORT와 Workarea 가능성
초기 응답앞쪽 고객부터 빠를 수 있음Window 처리 후 반환될 수 있음
전체 처리Outer가 크면 반복 비용 증가큰 Scan·Sort 비용 가능성

비교식은 다음처럼 단순화할 수 있습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
APPLY 비용 관점
= Outer 행 수 × 오른쪽 1회 Probe 비용

전체 집합 방식 비용 관점
= 주문 대상 범위 Scan
  + 고객별 순위 계산 또는 집계
  + 고객 집합과 Join

문법만 보고 우열을 정하지 않고 동일한 조건, 동일한 결과 행 수, 동일한 Fetch 범위에서 Runtime 통계를 비교합니다.


7. APPLY 내부 집계와 입력 0건

다음 Query는 고객별 주문 건수와 합계를 계산합니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT c.customer_id,
       x.order_count,
       x.total_amount
FROM   customers c
CROSS APPLY (
    SELECT COUNT(*)    AS order_count,
           SUM(amount) AS total_amount
    FROM   orders o
    WHERE  o.customer_id = c.customer_id
) x;

GROUP BY가 없는 전체 집계는 Filter 후 입력이 0건이어도 결과 행 하나를 만듭니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
COUNT(*)    → 0
SUM(amount) → NULL

따라서 주문이 없는 고객도 오른쪽 결과가 1행 존재하므로 CROSS APPLY에서 제거되지 않습니다.

주문이 있는 고객만 남기려면 집계 결과 자체가 0행이 되도록 제한합니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT c.customer_id,
       x.order_count
FROM   customers c
CROSS APPLY (
    SELECT COUNT(*) AS order_count
    FROM   orders o
    WHERE  o.customer_id = c.customer_id
    HAVING COUNT(*) > 0
) x;

HAVING을 통과하지 못하면 오른쪽 결과가 0행이 되어 해당 고객이 제거됩니다.


8. OUTER APPLY의 NULL과 미매칭 구분

OUTER APPLY 결과의 오른쪽 Column이 NULL이라고 해서 반드시 오른쪽 행이 없었다고 단정할 수 없습니다.

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
오른쪽 행 없음
→ 오른쪽 출력 Column 전체가 NULL로 확장

오른쪽 행 존재, AMOUNT 자체가 NULL
→ ORDER_ID는 값이 있고 AMOUNT만 NULL일 수 있음

매칭 존재 여부가 중요하면 실제 행이 존재할 때 반드시 값이 있는 식별 Column을 함께 반환합니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
CASE
  WHEN x.order_id IS NULL THEN 'NO_ORDER'
  ELSE 'HAS_ORDER'
END

업무상 ORDER_ID도 Nullable할 수 있다면 별도의 Match Indicator를 설계해야 합니다.


9. 실행계획으로 반복 비용 진단

9.1 Runtime 통계 수집과 출력

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT /*+ GATHER_PLAN_STATISTICS */
       c.customer_id,
       x.order_id,
       x.order_date
FROM   customers c
OUTER APPLY (
    SELECT o.order_id,
           o.order_date
    FROM   orders o
    WHERE  o.customer_id = c.customer_id
    ORDER BY o.order_date DESC,
             o.order_id   DESC
    FETCH FIRST 1 ROW ONLY
) x
WHERE  c.region_code = :region_code;
SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT *
FROM TABLE(
    DBMS_XPLAN.DISPLAY_CURSOR(
        NULL,
        NULL,
        'ALLSTATS LAST +PREDICATE +ALIAS +NOTE'
    )
);

ALLSTATS LAST는 마지막 실행의 I/O 및 Memory 관련 Runtime 통계를 확인하는 데 사용합니다. 통계가 수집되지 않았거나 Cursor가 다른 경우 원하는 값이 표시되지 않을 수 있으므로 대상 SQL_ID와 Child Cursor도 확인합니다.

9.2 확인 항목

항목의미와 판단 질문
Outer A-RowsAPPLY 입력이 된 왼쪽 실제 행 수는 얼마인가?
오른쪽 Root Starts해당 Row Source가 실제로 몇 번 시작됐는가?
오른쪽 A-Rows전체 Starts 동안 몇 행을 출력했는가?
Buffers해당 Operation이 누적한 Logical I/O는 얼마인가?
ReadsPhysical Read가 얼마나 발생했는가?
STOPKEY·NOSORT행 제한과 정렬 생략이 실제 Plan에 반영됐는가?
Predicate왼쪽 Key가 오른쪽 Index의 access 조건인가?

9.3 1회당 작업량 계산

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
오른쪽 1회당 평균 출력 행
= 오른쪽 A-Rows ÷ 오른쪽 Starts

오른쪽 1회당 평균 Logical I/O
= 오른쪽 Buffers ÷ 오른쪽 Starts

예를 들어 오른쪽 Operation의 Starts=100, A-Rows=100, Buffers=900이면 평균적으로 한 번 시작할 때 1행을 반환하고 9 Buffer를 사용한 것입니다.

Starts가 Outer A-Rows와 같지 않을 수도 있습니다. Optimizer가 Query를 변환하거나 다른 Join 구조를 선택할 수 있으므로 “APPLY 문법이므로 반드시 Outer 행 수만큼 Starts가 발생한다”고 단정하지 않고 실제 Plan을 기준으로 해석합니다.


10. 일반 Join이 더 명확한 경우

단순 등치 조인만 필요하고 오른쪽 Query Block에서 왼쪽 값에 따른 정렬·행 제한·함수 호출을 수행하지 않는다면 일반 Join이 의도를 더 직접적으로 보여 줍니다.

SQL코드 영역 안에서 좌우로 이동할 수 있습니다.
SELECT c.customer_id,
       o.order_id
FROM   customers c
LEFT JOIN orders o
  ON   o.customer_id = c.customer_id;

APPLY가 특히 유용한 대표 상황은 다음과 같습니다.

  • 왼쪽 Key별 Top-N
  • 왼쪽 Row에 따라 달라지는 Inline View
  • 왼쪽 값을 인수로 받는 Table Function
  • 오른쪽에서 여러 Aggregate나 여러 Column을 한 Query Block에서 계산
  • 일반 Inline View로는 표현할 수 없는 왼쪽 상관 참조

단순 Join을 APPLY로 바꾸는 것만으로 성능 개선이 보장되지는 않습니다.


11. 혼동하기 쉬운 판단

혼동하기 쉬운 판단정확한 기준
LATERAL은 왼쪽 행을 보존한다LATERAL은 상관 범위를 제공하고 보존 여부는 Join 방식이 결정한다
CROSS APPLY는 항상 절차적으로 한 행씩 실행된다논리적 왼쪽 상관을 제공하며 물리 Plan은 Optimizer가 결정한다
OUTER APPLY는 오른쪽에서 항상 한 행만 반환한다오른쪽이 N행이면 왼쪽 행도 N행으로 증가한다
CROSS APPLY 내부 집계는 매칭이 없으면 왼쪽 행을 제거한다GROUP BY 없는 집계는 입력 0건에서도 결과 1행을 만들 수 있다
날짜 내림차순만 지정하면 최신 한 건이 항상 동일하다동점이 가능하면 유일한 Tie-Breaker가 필요하다
APPLY는 일반 Join보다 항상 빠르다Outer 행 수, Probe 비용, Sort·Stopkey, Fetch 범위로 비교한다
오른쪽 Column이 NULL이면 미매칭이다실제 행의 Column 값이 NULL일 수 있으므로 식별 Column을 확인한다
Buffers만 보면 반복 1회의 비용을 알 수 있다누적 BuffersStarts로 나누어 1회당 비용을 함께 본다

12. 실전 적용 절차

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
1. 결과 한 행의 업무 의미를 정의한다.
2. 오른쪽 결과가 0·1·N건일 때 최종 행 수를 계산한다.
3. 미매칭 왼쪽 행 보존 여부로 CROSS/OUTER를 선택한다.
4. Top-N이면 유일한 Tie-Breaker를 포함한 ORDER BY를 작성한다.
5. 상관 Key + 정렬 Key 순서에 맞는 Index를 검토한다.
6. APPLY와 분석 함수·사전 집계 대안을 비교한다.
7. GATHER_PLAN_STATISTICS와 DBMS_XPLAN으로 Runtime 통계를 확인한다.
8. A-Rows/Starts와 Buffers/Starts로 1회당 작업량을 계산한다.
9. 0건·NULL·동점·대량 Outer 조건에서 결과와 성능을 회귀 검증한다.

핵심 정리

CODE코드 영역 안에서 좌우로 이동할 수 있습니다.
LATERAL
→ 오른쪽 Inline View에 왼쪽 상관 범위를 부여

CROSS APPLY
→ 오른쪽 결과가 있는 왼쪽 행만 반환

OUTER APPLY
→ 모든 왼쪽 행을 보존하고 미매칭 오른쪽 Column을 NULL로 확장

Key별 Top-N
→ 상관 Key + 결정적인 ORDER BY + 행 제한 + 적절한 Index

성능 진단
→ 문법 추측이 아니라 Starts·A-Rows·Buffers와 1회당 작업량 확인

공식 검증 기준

  • Oracle AI Database 26ai SQL Language Reference — SELECT, LATERAL, cross_outer_apply_clause, row_limiting_clause
  • Oracle AI Database 26ai SQL Language Reference — Aggregate Functions, COUNT
  • Oracle AI Database 26ai SQL Tuning Guide — Query Transformations
  • Oracle Database PL/SQL Packages and Types Reference — DBMS_XPLAN
  • 한국데이터산업진흥원 데이터자격시험 — SQL 전문가 시험주요내용
스스로 확인하기

개념 확인 문제

문제를 누르면 바로 아래에서 정답과 해설을 확인할 수 있습니다.

01LATERAL이 일반 Inline View와 다른 핵심 기능은 무엇인가?
정답 및 해설

오른쪽 Inline View가 FROM 절에서 자신보다 왼쪽에 정의된 Row Source의 Column을 참조할 수 있게 하는 왼쪽 상관 범위를 제공합니다. LATERAL 자체는 행 보존 방식을 결정하지 않습니다.

02오른쪽 결과가 0건일 때 CROSS APPLY와 OUTER APPLY는 각각 어떻게 동작하는가?
정답 및 해설

CROSS APPLY는 해당 왼쪽 행을 제거하고, OUTER APPLY는 왼쪽 행을 1행 보존하면서 오른쪽 출력 Column을 NULL로 확장합니다.

03오른쪽 결과 건수가 2, 0, 1일 때 CROSS APPLY와 OUTER APPLY의 전체 행 수는 각각 몇 행인가?
정답 및 해설

CROSS APPLY는 3행, OUTER APPLY는 4행입니다. CROSS APPLY2+0+1, OUTER APPLY2+1+1로 계산합니다.

04고객별 최근 주문 한 건 조회에서 ORDERDATE DESC 뒤에 ORDERID DESC를 추가하는 이유는 무엇인가?
정답 및 해설

같은 주문일시를 가진 행 사이의 순서를 유일하게 확정하기 위해서입니다. Tie-Breaker가 없으면 Top-1 결과가 결정적이지 않을 수 있습니다.

05(CUSTOMERID, ORDERDATE DESC, ORDERID DESC) Index가 Key별 Top-N에 유리한 이유는 무엇인가?
정답 및 해설

CUSTOMER_ID를 등치로 탐색한 뒤 같은 고객 범위에서 최신 ORDER_DATE 방향으로 읽고 ORDER_ID로 동점을 확정하여 첫 행에서 중단할 가능성을 높이기 때문입니다.

06CROSS APPLY 내부의 COUNT(), SUM(amount)가 주문 0건에서도 고객 행을 남길 수 있는 이유는 무엇인가?
정답 및 해설

GROUP BY 없는 전체 집계는 입력이 없어도 결과 행 하나를 만들기 때문입니다. 이때 COUNT(*)는 0, SUM(amount)NULL이므로 오른쪽 결과는 0행이 아닙니다.

07주문이 없는 고객도 보존하려면 어떤 APPLY를 선택해야 하는가?
정답 및 해설

OUTER APPLY를 선택합니다. 오른쪽에서 주문을 찾지 못해도 왼쪽 고객 행을 보존합니다.

08OUTER APPLY 결과에서 AMOUNT가 NULL일 때 미매칭 여부를 어떻게 구분해야 하는가?
정답 및 해설

ORDER_ID처럼 실제 오른쪽 행이 존재할 때 반드시 값이 있는 식별 Column을 함께 확인합니다. AMOUNT 자체가 NULL일 수 있으므로 AMOUNT IS NULL만으로 미매칭을 확정하면 안 됩니다.

09Starts=100, A-Rows=200, Buffers=900이면 오른쪽 1회당 평균 출력 행과 평균 Buffer는 얼마인가?
정답 및 해설

1회당 평균 출력 행은 2행이고 평균 Buffer는 9입니다. 각각 A-Rows ÷ Starts = 200 ÷ 100, Buffers ÷ Starts = 900 ÷ 100으로 계산합니다.

10APPLY와 ROWNUMBER 방식 중 어느 쪽이 더 빠른지 결정할 때 어떤 기준으로 비교해야 하는가?
정답 및 해설

동일한 조건과 결과 범위에서 Outer 행 수, 오른쪽 1회 Probe 비용, 전체 주문 Scan 범위, Sort·Workarea, Stopkey 여부, 실제 Starts·A-Rows·Buffers를 비교해야 합니다. 문법만으로 우열을 단정하지 않습니다.