-- L6 (b) child scalar IN projection — SELECT new carrier(h.feedItem.id, h.color, h.text, h.createdAt) -- FeedProjectionIT.l6ExplainProjectionHasLimitAndSemiJoinNotNarrowerWidth (seed 100, page size 20) -- SELECT h.feed_item_id, h.color, h.text, h.created_at FROM highlights h -- WHERE h.feed_item_id IN (SELECT fi.id FROM feed_items fi ORDER BY fi.first_highlighted_at DESC, fi.id ASC LIMIT 20) -- 읽기 포인트: Hash Semi Join 이라 자식 행(1509)만 반환 — 부모 M × 자식 K 로 곱하지 않는다(카테시안 없음). -- L5 배치 IN (b)와 같은 shape/행수(1509). 프로젝션은 필요 컬럼만(color/text/created_at) 읽는다. Hash Semi Join (cost=50.51..235.83 rows=177 width=686) (actual time=0.218..0.452 rows=1509 loops=1) Hash Cond: (h.feed_item_id = "ANY_subquery".id) Buffers: shared hit=202 -> Seq Scan on highlights h (cost=0.00..178.71 rows=1771 width=686) (actual time=0.148..0.226 rows=1961 loops=1) Buffers: shared hit=161 -> Hash (cost=50.26..50.26 rows=20 width=16) (actual time=0.067..0.067 rows=20 loops=1) Buckets: 1024 Batches: 1 Memory Usage: 9kB Buffers: shared hit=41 -> Subquery Scan on "ANY_subquery" (cost=50.01..50.26 rows=20 width=16) (actual time=0.060..0.062 rows=20 loops=1) Buffers: shared hit=41 -> Limit (cost=50.01..50.06 rows=20 width=24) (actual time=0.059..0.060 rows=20 loops=1) Buffers: shared hit=41 -> Sort (cost=50.01..50.62 rows=246 width=24) (actual time=0.059..0.059 rows=20 loops=1) Sort Key: fi.first_highlighted_at DESC, fi.id Sort Method: top-N heapsort Memory: 26kB Buffers: shared hit=41 -> Seq Scan on feed_items fi (cost=0.00..43.46 rows=246 width=24) (actual time=0.035..0.041 rows=100 loops=1) Buffers: shared hit=41 Planning Time: 0.064 ms Execution Time: 0.497 ms