Arelで自己相関サブクエリ
Rails 4.2.5 と MySQL 5.6.30 で確認。
「自己相関サブクエリ」という文言が適切かどうかわかっていないので、もし間違っていればご指摘願います。
やりたかったのは、同一テーブルの中で、ある要素によってグルーピングした集団のそれぞれから特定のレコードを取得するということ。
つまり以下みたいなの。
SELECT * FROM prices AS p1 WHERE NOT EXISTS ( SELECT 1 FROM prices AS p2 WHERE p1.product_id = p2.product_id AND p1.created_at < p2.created_at );
以下のようなproducts(商品)テーブルと、
id | name |
---|---|
1 | りんご |
2 | バナナ |
3 | みかん |
時系列で管理されるprices(値段)テーブルがあったとして、
id | product_id | value | created_at |
---|---|---|---|
1 | 1 | 80 | 2020-04-12 |
2 | 2 | 90 | 2020-04-18 |
3 | 3 | 30 | 2020-04-20 |
4 | 1 | 120 | 2020-04-20 |
5 | 2 | 100 | 2020-04-21 |
6 | 1 | 100 | 2020-04-25 |
この中から各商品の最新の値段を取得したい。すなわち結果として以下を得たい。
id | product_id | value | created_at |
---|---|---|---|
3 | 3 | 30 | 2020-04-20 |
5 | 2 | 100 | 2020-04-21 |
6 | 1 | 100 | 2020-04-25 |
生のSQLを渡すのは抵抗があるので、Arelでなんとかしたい。
TableAliasを使えばよさげ。
> arel_table = Price.arel_table > table_alias = arel_table.alias > latest_prices = Price.where(arel_table.project(1).from(table_alias) .where(arel_table[:product_id].eq(table_alias[:product_id])) .where(arel_table[:created_at].lt(table_alias[:created_at])) .exists.not) #=> [#<Price:0x00555b82b03398 id: 3, product_id: 3, value: 30, created_at: Mon, 20 Apr 2020 00:00:00 UTC +00:00>, # #<Price:0x00555b82b03230 id: 5, product_id: 2, value: 100, created_at: Tue, 21 Apr 2020 00:00:00 UTC +00:00>, # #<Price:0x00555b82b030c8 id: 6, product_id: 1, value: 100, created_at: Sat, 25 Apr 2020 00:00:00 UTC +00:00>]
できた。
to_sql
すると以下みたいな感じ。
SELECT `prices`.* FROM `prices` WHERE (NOT (EXISTS ( SELECT 1 FROM `prices` `prices_2` WHERE `prices`.`product_id` = `prices_2`.`product_id` AND `prices`.`created_at` < `prices_2`.`created_at`)))
自動で prices_2 になってた別名は alias
呼ぶときに引数を渡すことで任意の名前を指定できる。
table_alias = arel_table.alias(:hoge)
SELECT `prices`.* FROM `prices` WHERE (NOT (EXISTS ( SELECT 1 FROM `prices` `hoge` WHERE `prices`.`product_id` = `hoge`.`product_id` AND `prices`.`created_at` < `hoge`.`created_at`)))
参考
同一グループの中で最大のレコードを取得する SQL を書く - TIM Labs
ActiveRecord4でこんなSQLクエリどう書くの? Arel編 - TIM Labs