📝

DISTINCTとGROUP BY

2025/01/10に公開

Daily Blogging20日め

今日は技術的なお話だ!
Railsのクエリのお話
業務でクエリの修正が必要になって、重複データを取り除くクエリにする必要があった
重複といえばDISTINCTとGROUP BYがパッと思いついたけど、パフォーマンス的にはどっちがいいんだっけってなった
今回はidの重複がとり除ければ良かった

結論

今回はGROUP BYの方がパフォーマンスが良かった。
※テーブル名とかは適当に変えたのでどっか間違ってるかも

GROUP BY

Product.joins(:product_categories).group("products.id")
Group  (cost=7228.12..7316.54 rows=371 width=41)
   Group Key: products.id
   ->  Gather Merge  (cost=7228.12..7314.69 rows=742 width=41)
         Workers Planned: 2
         ->  Sort  (cost=6228.09..6229.02 rows=371 width=41)
               Sort Key: products.id
               ->  Partial HashAggregate  (cost=6208.55..6212.26 rows=371 width=41)
                     Group Key: products.id
                     ->  Hash Join  (cost=11.35..5870.13 rows=135368 width=41)
                           Hash Cond: (product_categories.product_id = products.id)
                           ->  Parallel Seq Scan on product_categories  (cost=0.00..5499.38 rows=135368 width=8)
                                 Filter: ((start_at <= '2025-01-10 16:35:20.286284'::timestamp without time zone) AND (('2025-01-10 16:35:20.286284'::timestamp without time zone < end_at) OR (end_at IS NULL)))
                           ->  Hash  (cost=6.71..6.71 rows=371 width=41)
                                 ->  Seq Scan on products  (cost=0.00..6.71 rows=371 width=41)
(14 rows)

DISTINCT

Product.joins(:product_categories).distinct("products.id")
HashAggregate  (cost=12744.67..12748.38 rows=371 width=41)
   Group Key: products.id, products.name, products.created_at, products.updated_at
   ->  Hash Join  (cost=11.35..9495.83 rows=324884 width=41)
         Hash Cond: (product_categories.product_id = products.id)
         ->  Seq Scan on product_categories  (cost=0.00..8621.92 rows=324884 width=8)
               Filter: ((start_at <= '2025-01-10 16:35:30.066085'::timestamp without time zone) AND (('2025-01-10 16:35:30.066085'::timestamp without time zone < end_at) OR (end_at IS NULL)))
         ->  Hash  (cost=6.71..6.71 rows=371 width=41)
               ->  Seq Scan on products  (cost=0.00..6.71 rows=371 width=41)
(8 rows)

二つの違い

DISTINCTだと、idだけじゃなくて、他の値の組み合わせもユニークなレコードを抽出しようとしているので、GROUP BYより処理時間がかかっている。

GROUP BYの方は、Gather Mergeの箇所で並列で処理されているので処理速度が速い

Discussion