상황: 회원별 2023년 12월 주문금액을 SELECT 절의 스칼라 서브쿼리로 조회한다. 집계 함수를 생략해도 지금은 정상 실행되는 회원이 있지만, 주문이 여러 건인 회원을 만나면 오류가 발생한다. 실제 일반 테이블 startdbpg.ms_mbr, startdbpg.tr_ord에서 해당 사례를 찾아 실행했다.
실제 주문 데이터 (2023-12-01 이상 ~ 2024-01-01 미만)
회원ID | 12월 주문 건수 | 실제 주문금액 | 스칼라 서브쿼리 |
M0406 | 0건 | 없음 | 정상, NULL |
M0036 | 1건 | 14,000 | 정상, 14000.000 |
M0039 | 2건 | 15,500 / 10,500 | 오류 발생 |
정상 실행 — 집계 함수가 없어도 서브쿼리가 0~1행을 반환하는 경우
SELECT m.mbr_id,
(SELECT o.ord_amt
FROM startdbpg.tr_ord o
WHERE o.mbr_id = m.mbr_id
AND o.ord_dtm >= TIMESTAMP '2023-12-01'
AND o.ord_dtm < TIMESTAMP '2024-01-01') ord_amt
FROM startdbpg.ms_mbr m
WHERE m.mbr_id IN ('M0406', 'M0036')
ORDER BY m.mbr_id;
SQL
복사
실제 실행 결과
mbr_id | ord_amt
-------+----------
M0036 | 14000.000
M0406 | NULL
Plain Text
복사
•
M0036: 서브쿼리가 1행을 반환해 주문금액이 정상 표시된다.
•
M0406: 서브쿼리가 0행을 반환해 스칼라 값은 NULL이 된다.
•
중요: M0036에 현재 주문이 1건 있다는 것은 미래에도 1건이라는 보장이 아니다. 주문이 추가되면 같은 SQL이 갑자기 실패할 수 있다.
BAD SQL — 스칼라 서브쿼리가 2행 이상 반환하는 경우
SELECT m.mbr_id,
(SELECT o.ord_amt
FROM startdbpg.tr_ord o
WHERE o.mbr_id = m.mbr_id
AND o.ord_dtm >= TIMESTAMP '2023-12-01'
AND o.ord_dtm < TIMESTAMP '2024-01-01') ord_amt
FROM startdbpg.ms_mbr m
WHERE m.mbr_id = 'M0039';
SQL
복사
실제 실행 오류
ERROR: more than one row returned by a subquery used as an expression
SQLSTATE: 21000
Plain Text
복사
실제 M0039의 해당 월 주문은 2건이다.
ord_no | ord_amt
-------+----------
499212 | 15500.000
499752 | 10500.000
Plain Text
복사
GOOD SQL — 업무 목적이 '주문금액 합계'라면 SUM으로 단일 값 반환
SELECT m.mbr_id,
(SELECT SUM(o.ord_amt)
FROM startdbpg.tr_ord o
WHERE o.mbr_id = m.mbr_id
AND o.ord_dtm >= TIMESTAMP '2023-12-01'
AND o.ord_dtm < TIMESTAMP '2024-01-01') ord_amt
FROM startdbpg.ms_mbr m
WHERE m.mbr_id = 'M0039';
SQL
복사
실제 실행 결과: M0039 / 26000.000. SUM은 해당 월 주문이 몇 건이든 집계 결과 한 행·한 컬럼을 반환하며, 0건이면 합계는 NULL이다.
다른 요구사항 — '가장 최근 주문 1건의 금액'이라면
SELECT m.mbr_id,
(SELECT o.ord_amt
FROM startdbpg.tr_ord o
WHERE o.mbr_id = m.mbr_id
AND o.ord_dtm >= TIMESTAMP '2023-12-01'
AND o.ord_dtm < TIMESTAMP '2024-01-01'
ORDER BY o.ord_dtm DESC, o.ord_no DESC
LIMIT 1) ord_amt
FROM startdbpg.ms_mbr m
WHERE m.mbr_id = 'M0039';
SQL
복사
실제 실행 결과: M0039 / 10500.000. 마지막 주문의 판정 기준을 ord_dtm DESC, ord_no DESC로 명확하게 지정했다. 오류를 피하려고 무작정 LIMIT 1을 붙여서는 안 된다. 최신 한 건이 업무 목적일 때만 사용한다.
핵심
•
스칼라 서브쿼리는 반드시 최대 1행·1컬럼을 반환해야 한다. 집계 함수가 없다고 무조건 오류가 나는 것도 아니고, 현재 정상 실행된다고 안전한 것도 아니다. 관련 데이터가 나중에 늘어나도 1행을 보장할 수 있는지 확인하자.
참고·예외
•
서브쿼리 조건이 ord_no처럼 PK·UK에 의해 유일한 행을 지정하면 집계 함수 없이도 안전할 수 있다. 반면 일반적인 회원ID나 기간 조건은 주문이 여러 건일 수 있으므로 단일 행을 보장하지 않는다. 다수 주문의 합계가 목적이면 SUM 등의 집계를, 최신 한 건이 목적이면 결정적인 ORDER BY ... LIMIT 1을 사용한다. 전체 회원의 반복 집계 성능 문제는 06-02에서 별도로 다룬다. 이 항목은 성능보다 잠재적 런타임 오류 예방이 목적이다.


