NVL(MAX) 쿼리 결과가 이상합니다 0 7 2,397

by 양양 [SQL Query] [2023.01.19 20:44:08]


정상.jpg (554,140Bytes)
비정상.jpg (873,398Bytes)

안녕하세요. 구루비에서 많은 도움을 얻고 있습니다. 감사드립니다.

 

M_CUS(고객) 와 T_ORD(주문) 테이블은 1:N 관계입니다.

2017년 3월에 주문 이력이 있는 고객 정보를 얻는 것이 목표입니다.

 

<교재 모범 쿼리>

SELECT  T1.CUS_ID ,T1.CUS_NM
            ,(CASE  WHEN
                      EXISTS(
                          SELECT  *
                          FROM    T_ORD A 
                          WHERE   A.CUS_ID = T1.CUS_ID 
                          AND     A.ORD_DT >= TO_DATE('20170301','YYYYMMDD')
                          AND     A.ORD_DT < TO_DATE('20170401','YYYYMMDD')
                          ) 
              THEN 'Y' 
              ELSE 'N' END) ORD_YN_03

    FROM    M_CUS T1
    ORDER BY 1;

 

<임의 변경>

SELECT  T1.CUS_ID ,T1.CUS_NM
            , (
                SELECT NVL( MAX('Y'),'N')
                FROM    T_ORD A 
                 WHERE   A.CUS_ID = T1.CUS_ID 
                AND     A.ORD_DT >= TO_DATE('20170301','YYYYMMDD')
                AND     A.ORD_DT < TO_DATE('20170401','YYYYMMDD')

--                AND ROWNUM <= 1 -- 해당 조건을 붙이는 것이 맞으나

--                                                실수로 주석처리 하였더니 위와 다른 결과가 나옴
               ) "ORD_YN_03"
    FROM    M_CUS T1
    ORDER BY 1;
 

ROWNUM 라인을 주석을 풀면 원하는 결과가 나오지만,

ROWNUM 라인 주석 해제시 원하는 결과가 나오지 않습니다. (결과가 달라짐)

 

제가 3시간 이상 생각해 보았을 때는 도저히 차이를 모르겠는데 어떤 이유인지 힌트라도 부탁드려도 될까요...

감사합니다.

 

 

by 마농 [2023.01.20 08:14:47]
WITH m_cus AS
(
SELECT 1 cus_id, 'A' cus_nm FROM dual
UNION ALL SELECT 2, 'B' FROM dual
UNION ALL SELECT 3, 'C' FROM dual
UNION ALL SELECT 4, 'D' FROM dual
)
, t_ord AS
(
SELECT 1 cus_id, DATE '2017-03-01' ord_dt FROM dual
UNION ALL SELECT 1, DATE '2017-03-02' FROM dual
UNION ALL SELECT 1, DATE '2017-03-03' FROM dual
UNION ALL SELECT 2, DATE '2017-03-04' FROM dual
UNION ALL SELECT 3, DATE '2017-04-01' FROM dual
)
SELECT t1.cus_id
     , t1.cus_nm
     , CASE WHEN EXISTS (SELECT *
                           FROM t_ord a
                          WHERE a.cus_id = t1.cus_id
                            AND a.ord_dt >= TO_DATE('20170301', 'yyyymmdd')
                            AND a.ord_dt <  TO_DATE('20170401', 'yyyymmdd')
                         ) 
       THEN 'Y' ELSE 'N' END ord_yn_01
     , (SELECT NVL(MAX('Y'), 'N')
          FROM t_ord a
         WHERE a.cus_id = t1.cus_id
           AND a.ord_dt >= TO_DATE('20170301', 'yyyymmdd')
           AND a.ord_dt <  TO_DATE('20170401', 'yyyymmdd')
        ) ord_yn_02
     , (SELECT NVL(MAX('Y'), 'N')
          FROM t_ord a
         WHERE a.cus_id = t1.cus_id
           AND a.ord_dt >= TO_DATE('20170301', 'yyyymmdd')
           AND a.ord_dt <  TO_DATE('20170401', 'yyyymmdd')
           AND ROWNUM <= 1
        ) ord_yn_03
  FROM m_cus t1
 ORDER BY 1
;
-- 결과 동일함 --
1 A Y Y Y
2 B Y Y Y
3 C N N N
4 D N N N

 


by 양양 [2023.01.20 10:19:45]

n/a

 


by 마농 [2023.01.20 10:38:55]

테이블 생성헤서 테스트 해봤는데 결과 동일합니다. 정상.


by 양양 [2023.01.20 11:01:18]

마농님. 테스트해본 결과 버그인 것 같습니다.

두 환경 12.2(no patch), 18c(no patch) 에서 테스트했을 때는 똑같이 "비정상.jpg" 처럼 나왔는데

 

psu가 적용된 실제 서버에서 테스트해보니 올바른 결과가 나오네요. "정상.jpg" 처럼 올바른 결과가 나오네요

- 12c psu 적용 / 19c psu 적용


by 양양 [2023.01.20 11:07:15]

메인글의 첨부파일에 "비정상.jpg"로 업로드 하였습니다.

위 스크립트로 저도 새로 만들어서 구조가 다를 가능성도 없는데 신기하네요...


by ㅇㅇ준 [2023.01.25 11:19:29]

AND ROWNUM <= 1

실행계획에 따라 다른 1row를 가져올 수 있는데 order by 로 명시적 조건을 부여하는것이 어떠실까요?


by 마농 [2023.01.25 13:42:57]

결국 SELECT 절에서는 'Y' 를 조회하므로
- 어떤 행을 가져오든 결과는 'Y' 가 됩니다.
- 어떤 행도 못 가져오면 결과는 'N' 이 됩니다.
즉, ROWNUM 에 의해 가져오는 행이 달라진다고 결과가 달라지지는 않습니다.
즉, 이 문제는 존재여부를 체크하는 쿼리로
어떤 행을 가져오는지가 중요한게 아니라 행이 존재하는지가 중요합니다.
존재여부만 판명하므로 1행만 가져오는게 유리하므로 ROWNUM 조건을 주는게 맞긴한데.
ROWNUM 안준다고 해서 결과가 달라져서는 안되는데, 결과가 달라지는 버그가 있었던 것 같습니다.

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입