9-8-1. LATERAL
MySQL은 8.0.35는 LATERAL 기능을 제공한다. DBMS와 버젼에 따라 LATERAL 기능은 제공하지 않을 수도 있다. 기본적으로 인라인 뷰에서는 외부 쿼리의 조건 절을 받아서 처리할 수 없다. 하지만 LATERAL을 적용하면 인라인 뷰에서 인라인 뷰 바깥쪽 쿼리의 조건 절을 받아 처리할 수 있다. 이너 조인과 아우터 조인 모두 LATERAL을 적용할 수 있다.
-- [SQL-9-8-1-a]
-- 일반 INNER JOIN, 인라인 뷰에서 ShopId별 GROUP BY후 1:1로 조인 처리
SELECT T1.ShopId ,T1.ShopNm ,T1.ShopSize
,T2.SUM_OrdAmt
FROM startdb.Shop T1
INNER JOIN (
SELECT A.ShopId
,SUM(A.OrdAmt) SUM_OrdAmt
FROM startdb.Ord A
WHERE A.OrdDtm >= STR_TO_DATE('20221001','%Y%m%d')
AND A.OrdDtm < STR_TO_DATE('20221201','%Y%m%d')
GROUP BY A.ShopID
) T2
ON (T2.ShopID = T1.ShopId)
WHERE T1.ShopStartYmd = '20180327';
-- [SQL-9-8-1-b]
-- 인라인 뷰에는 메인 쿼리의 startdb.Shop T1의 조건을 적어서 사용할 수 없다.
-- 아래와 같이 SQL을 실행하면 에러가 발생한다.
SELECT T1.ShopId ,T1.ShopNm ,T1.ShopSize
,T2.SUM_OrdAmt
FROM startdb.Shop T1
INNER JOIN (
SELECT A.ShopId
,SUM(A.OrdAmt) SUM_OrdAmt
FROM startdb.Ord A
WHERE A.OrdDtm >= STR_TO_DATE('20221001','%Y%m%d')
AND A.OrdDtm < STR_TO_DATE('20221201','%Y%m%d')
AND A.ShopId = T1.ShopId -- > 에러 발생, 외부의 컬럼을 사용할 수 없다.
GROUP BY A.ShopID
) T2
ON (T2.ShopID = T1.ShopId)
WHERE T1.ShopStartYmd = '20180327';
-- [SQL-9-8-1-c]
-- JOIN에 LATERAL 키워드를 추가하면 조건을 받아서 처리할 수 있다.
SELECT T1.ShopId ,T1.ShopNm ,T1.ShopSize
,T2.SUM_OrdAmt
FROM startdb.Shop T1
INNER JOIN LATERAL ( -- > LATERAL 키워드 추가
SELECT A.ShopId
,SUM(A.OrdAmt) SUM_OrdAmt
FROM startdb.Ord A
WHERE A.OrdDtm >= STR_TO_DATE('20221001','%Y%m%d')
AND A.OrdDtm < STR_TO_DATE('20221201','%Y%m%d')
AND A.ShopId = T1.ShopId -- > LATERAL 키워드를 사용해 조건을 받을 수 있다.
GROUP BY A.ShopID
) T2
ON (T2.ShopID = T1.ShopId)
WHERE T1.ShopStartYmd = '20180327';
SQL
복사
마찬가지로 OUTER JOIN에도 LATERAL 을 사용할 수 있다.
-- [SQL-9-8-1-d]
SELECT T1.ShopId ,T1.ShopNm ,T1.ShopSize
,T2.SUM_OrdAmt
FROM startdb.Shop T1
LEFT OUTER JOIN LATERAL (
SELECT A.ShopId
,SUM(A.OrdAmt) SUM_OrdAmt
FROM startdb.Ord A
WHERE A.OrdDtm >= STR_TO_DATE('20221001','%Y%m%d')
AND A.OrdDtm < STR_TO_DATE('20221201','%Y%m%d')
AND A.ShopId = T1.ShopId -- > LATERAL 키워드를 사용해 조건을 받을 수 있다.
GROUP BY A.ShopID
) T2
ON (T2.ShopID = T1.ShopId)
WHERE T1.ShopId IN ('S070','S071');
SQL
복사
LATERAL은 MySQL에서 성능 향상을 위해 사용하기도 한다. MySQL에서 인라인 뷰는 복잡도에 따라 메인 쿼리와 결합되지 못하고 내부적으로 완전히 별도 처리된다. 이로 인해 인라인 뷰에서 성능 저하가 발생될 수 있다. 아래 SQL들을 살펴보기 바란다.
-- [SQL-9-8-1-e]
-- 인라인 뷰에서 모든 매장의 주문 데이터를 SUM 처리
-- 필자 환경에서는 5.2초 정도 소요
SELECT T1.ShopId ,T1.ShopNm ,T1.ShopSize
,T2.SUM_OrdAmt
FROM startdb.Shop T1
INNER JOIN (
SELECT A.ShopId
,SUM(A.OrdAmt) SUM_OrdAmt
FROM startdb.Ord A
GROUP BY A.ShopID
) T2
ON (T2.ShopID = T1.ShopId)
WHERE T1.ShopStartYmd = '20180327';
-- [SQL-9-8-1-f]
-- LATERAL을 사용해 인라인 뷰에서 외부 조건을 받아서 처리
-- 특정 매장의 주문에 대해서만 SUM 처리 된다.
-- 필자 환경에서 0.07초 소요
SELECT T1.ShopId ,T1.ShopNm ,T1.ShopSize
,T2.SUM_OrdAmt
FROM startdb.Shop T1
INNER JOIN LATERAL (
SELECT A.ShopId
,SUM(A.OrdAmt) SUM_OrdAmt
FROM startdb.Ord A
WHERE A.ShopId = T1.ShopID -- > LATERAL 키워들 사용, 외부 조건을 적용
GROUP BY A.ShopID
) T2
ON (T2.ShopID = T1.ShopId)
WHERE T1.ShopStartYmd = '20180327';
SQL
복사
LATERAL 키워드를 추가하자 저절로 속도가 빨라졌다. 참고로 필자의 환경은 MySQL 8.0.35 윈도우즈 버젼이다. 버젼이나 DBMS에 따라 속도가 향상되지 않을 수도 있다. 또한 SQL에 따라서는 LATERAL을 추가하면 오히려 느려질 수도 있다. 성능 향상을 원한다면 실행계획을 통해 문제점을 찾고 해결해야 한다. LATERAL을 사용하면 좋아진다고 외우는건 아무 도움이 되지 않는다. 나중에 성능 부분도 공부해 보기 바란다.
LATERAL은 인라인 뷰가 메인 쿼리에 영향을 받도록 작성할 수 있으며, 이로 인해 조건에 따라 인라인 뷰의 결과가 동적이라는 점이다. 아래 SQL은 Shop별로 주문 수량이 가장 많은 상품 Top-3만 조회하는 SQL이다. 아래의 패턴은 SQL을 오래전부터 해온 이들에게는 굉장히 생소한 패턴이다. 문제가 있을만한 패턴은 아니기 때문에 필요하다면 충분히 사용할만하다.
-- [SQL-9-8-1-g]
SELECT T1.ShopId ,T1.ShopNm ,T1.ShopSize
,T2.ItemId ,T2.SUM_OrdAmt
FROM startdb.Shop T1
INNER JOIN LATERAL (
SELECT A.ShopId ,B.ItemId
,SUM(A.OrdAmt) SUM_OrdAmt
FROM startdb.Ord A
INNER JOIN startdb.OrdDet B
ON (A.OrdNo = B.OrdNo)
WHERE A.OrdDtm >= STR_TO_DATE('20221001','%Y%m%d')
AND A.OrdDtm < STR_TO_DATE('20221201','%Y%m%d')
AND A.ShopId = T1.ShopID -- > LATERAL 키워들 사용, 외부 조건을 적용
GROUP BY A.ShopID ,B.ItemId
LIMIT 3
) T2
ON (T2.ShopID = T1.ShopId)
WHERE T1.ShopStartYmd = '20180327';
ShopId ShopNm ShopSize ItemId SUM_OrdAmt
------ ------------- -------- ------ ----------
S014 Columbus-1st 53 AMB 43500.000
S014 Columbus-1st 53 LEMR 36000.000
S014 Columbus-1st 53 ICLB 32000.000
S028 San Diego-2nd 81 ICLR 25500.000
S028 San Diego-2nd 81 AMR 17000.000
S028 San Diego-2nd 81 CMFR 12000.000
S031 Austin-2nd 87 IAMB 32500.000
S031 Austin-2nd 87 LEMR 50500.000
S031 Austin-2nd 87 IAMR 35000.000
SQL
복사