고객 목록에 고객별 최근 주문 1건에 대한 요약 정보를 보여주세요.
라는 요구사항이 있습니다.
“고객” 객체와 “주문” 객체간의 관계는 다음과 같습니다.
+------------+ 0..* +------------+
| Customer | <>-----------> | Order |
+------------+ +------------+
애플리케이션 레이어에서 구현한다면, ORM을 통해 구한 Collection<Customer>
을 순회하면서, Customer
객체의 멤버 필드인 Collection<Order>
를 대상으로 최근 Order 객체만 필터링했거나, 적절한 순서로 정렬하여 뽑아 쓰기 쉽도록 했을겁니다. 요런 느낌으로요(검증 안된 Pseudo Code 입니다).
// Service Layer
public function listCustomers()
{
return $customers->map(function (Customer $customer) {
$sorted = $customer->orders->sortByDesc('id')->values();
$customer->setRelation('orders', $sorted);
return $customer;
});
}
// Controller/View 등
foreach ($customers as $customer) {
$lastOrder = $customer->orders->first();
$lastOrder->order_number; // 최근 주문 번호
}
문제점들이 눈에 띕니다.
- 시간복잡도는 O(m x n)입니다.
- CPU와 메모리를 혹사시킵니다.
Customer.orders: Collection<Order>
를 미리 로드하지 않았다면, N + 1 문제가 발생합니다.- 이 외에도 제가 보지 못한 문제점들이 더 있을 겁니다…
읽기 전용 쿼리이므로 ORM을 쓰지 않아도 됩니다. 싸고, 빠르고, 안전하게 SQL만으로 뽑아내는 방법을 찾아봤습니다.