Lateral join은 PostgreSQL 9.3, MySQL 8버전부터 사용 가능한 구문으로, 상위 조회 결과에 JOIN되는 하위 결과를 원하는 개수만큼만 조회하고 싶을 때에 주로 사용한다.
예를 들어서, 상품과 상품 옵션을 JOIN할 것인데, 상품 옵션의 판매 가격이 높은 순으로 2개만 JOIN하고 싶은 것과 같이 JOIN 대상 중, 일부만 JOIN하고 싶을 때 사용할 수 있다.
create table temp_product
(
product_id bigint unsigned auto_increment primary key,
product_name varchar(255) not null
);
create table temp_product_option
(
product_option_id bigint unsigned auto_increment primary key,
product_id bigint unsigned not null,
option_name varchar(255) not null,
price integer not null default 0
);
상품과 상품의 옵션을 관리하는 테이블을 만든다고 가정해본다.
insert into temp_product
(product_id, product_name)
values (1, '상품1'),
(2, '상품2'),
(3, '상품3');
insert into temp_product_option
(product_option_id, product_id, option_name, price)
values (1, 1, '상품1-옵션1', 10000),
(2, 1, '상품1-옵션2', 30000),
(3, 1, '상품1-옵션3', 20000),
(4, 2, '상품2-옵션1', 1000),
(5, 2, '상품2-옵션2', 40000),
(6, 3, '상품3-옵션1', 10000);
상품1에는 3개의 옵션, 상품2에는 2개의 옵션, 상품 3에는 1개의 옵션을 생성해준다.
select *
from temp_product p
left join temp_product_option po on p.product_id = po.product_id
order by p.product_id, po.product_option_id;
상품과 상품 옵션을 left join해서, 정상적으로 데이터가 생성되었고 맵핑되었는지 확인한다.

select *
from temp_product p
left join lateral (
select *
from temp_product_option
where product_id = p.product_id
order by price desc
limit 2
) po on true
order by p.product_id, po.product_option_id
예를 들어서, 가격이 높은 순으로 2개의 옵션만을 JOIN하여 뭔가를 하고 싶은 상황이라고 가정해보겠다. 글 초반에 말한 것 처럼 바로 이런 경우 Lateral join을 사용할 수 있다. 위 쿼리를 실행해보자.

예상했던 대로, 상품별로 가격이 높은 옵션이 최대 2개씩 JOIN이 된 것을 확인할 수 있다.
상품1은 2개의 옵션, 상품2는 옵션이 2개 밖에 없으므로 2개의 옵션 전부가 JOIN되었고, 상품3은 옵션이 1개 밖에 없으므로 1개의 옵션이 JOIN된 것을 확인할 수 있다.
이번 글에서는 Lateral join의 대표적인 사용 사례인 상위 N개를 JOIN하는 것에 대해서 알아보았는데, 배열 컬럼을 Spread한 결과를 Lateral join해서 배열이 펼쳐진(?) 상태로 쿼리하고 싶은 경우나, 동일 테이블 내에서 특정 조건을 만족하는 데이터를 JOIN하고 싶은 경우에도 사용할 수 있다.
이렇게 짧은 글이지만 아주 유용하게 사용할 수 있는 Lateral join에 대해서 알아보았다. ㅎㅎ