📝
DISTINCTとGROUP BY
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