-- 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 (<page-20 ids>) 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
