회사에서 MySQL 버전을 8.0 버전대로 올리면서 LATERAL JOIN을 사용할 수 있게 되었습니다.
당장 해당 조인을 사용해야 하는 요구사항을 받지는 못했지만, 어떤 방식으로 동작하는지를 미리 알아보고자 하였습니다.
조인에서 outer 테이블에 있는 각 행을 기준으로 inner 서브쿼리를 실행할 수 있도록 해주는 기능입니다.
일반적인 JOIN과는 다르게 outer 테이블의 행의 정보를 inner 서브쿼리에서 참조할 수 있다는 특징이 있습니다.
만약, 고객별로 최근 주문 1건을 조회하고 싶은 상황이라면 어떻게 쿼리를 작성해야 할까요?
SELECT
u.id,
u.name,
(SELECT o.id FROM orders o WHERE o.customer_id = u.id ORDER BY o.created_at DESC LIMIT 1) as order_id,
(SELECT o.created_at FROM orders o WHERE customer_id = u.id ORDER BY o.created_at DESC LIMIT 1) as ordered_datetime
FROM users u;
위와 같이 작성을 해야 합니다. SELECT 절에 서브쿼리를 반복적으로 사용하는 것은 가독성도 떨어집니다. 또한, 서브쿼리의 SELECT 절만 다르고 나머지는 동일한 쿼리를 users 행 하나마다 두 번씩 매번 실행해야 하는 점은 성능상 비효율적일 수 있습니다.
이를 LATERAL JOIN으로 다음과 같이 개선할 수 있습니다.
SELECT
u.id,
u.name,
o.id as order_id,
o.created_at as ordered_datetime
FROM users u
LEFT JOIN LATERAL (
SELECT
o.id,
o.created_at
FROM orders o
WHERE o.customer_id = u.id
ORDER BY o.created_at DESC
LIMIT 1
) o ON TRUE;
위에서 LATERAL JOIN으로 개선한 쿼리는 기존 쿼리에 비해 다음과 같은 장점을 가지고 있습니다.
LATERAL JOIN은 결국 행을 하나 찾을 때마다 서브쿼리를 실행해야 하는 구조입니다.
기존 서브쿼리 JOIN과는 다르게 외부 행마다 서브쿼리를 실행하므로 결과가 항상 동적으로 생성됩니다.
따라서 극단적으로 레코드 단위마다 서브 쿼리 결과를 위한 임시 테이블 생성이 진행될 수 있습니다.
무분별하게 사용하는 경우 성능 문제가 발생할 수 있으므로 주의해서 사용해야 합니다.