-- L14 (c) two-step IN + app-side cut — correct result but transfers ALL page-parent highlights -- FeedTopNIT.l14ExplainThreeWayPlanCompareIsTheCrownJewel (seed 1000, page 20) -- SELECT h.feed_item_id, h.color, h.text, h.created_at FROM highlights h -- WHERE h.feed_item_id IN () ORDER BY h.feed_item_id, h.created_at DESC -- 읽기 포인트: window 와 동일한 Hash Semi Join(rows=1509) — 다만 위에 WindowAgg 가 없어 1509행을 전량 -- 애플리케이션으로 전송한다(앱에서 부모별 top-3 컷). buffers shared hit=430 (window 과 동일 = 같은 스캔). -- = L6 프로젝션이 남긴 잔여(childRows=1509)의 정체. 전송 낭비: 60행이면 되는데 1509행. Sort (cost=531.52..532.49 rows=388 width=686) (actual time=1.586..1.625 rows=1509 loops=1) Sort Key: h.feed_item_id, h.created_at DESC Sort Method: quicksort Memory: 155kB Buffers: shared hit=430 -> Hash Semi Join (cost=172.47..514.84 rows=388 width=686) (actual time=0.493..0.833 rows=1509 loops=1) Hash Cond: (h.feed_item_id = "ANY_subquery".id) Buffers: shared hit=430 -> Seq Scan on highlights h (cost=0.00..327.85 rows=3885 width=686) (actual time=0.239..0.363 rows=2917 loops=1) Buffers: shared hit=289 -> Hash (cost=172.22..172.22 rows=20 width=16) (actual time=0.251..0.252 rows=20 loops=1) Buffers: shared hit=141 -> Subquery Scan on "ANY_subquery" (cost=171.97..172.22 rows=20 width=16) (actual time=0.239..0.241 rows=20 loops=1) -> Limit (cost=171.97..172.02 rows=20 width=24) (actual time=0.238..0.240 rows=20 loops=1) -> Sort (cost=171.97..174.09 rows=846 width=24) (actual time=0.238..0.238 rows=20 loops=1) Sort Key: fi.first_highlighted_at DESC, fi.id -> Seq Scan on feed_items fi (cost=0.00..149.46 rows=846 width=24) (actual time=0.112..0.175 rows=1000 loops=1) Planning Time: 0.060 ms Execution Time: 1.686 ms