Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Arel: Left outer join using symbols

I have this use case where I get the symbolized deep associations from a certain model, and I have to perform certain queries that involve using outer joins. How can one do it WITHOUT resorting to write the full SQL by hand?

Answers I don't want: - using includes (doesn't solve deep associations very well ( .includes(:cars => [:windows, :engine => [:ignition]..... works unexpectedly ) and I don't want its side-effects) - writing the SQL myself (sorry, it's 2013, cross-db support, etc etc..., and the objects I fetch are read_only, more side-effects)

I'd like to have an Arel solution. I know that using the arel_table's from the models I can construct SQL expressions, there's also a DSL for the joins, but somehow i cannot use it in the joins method from the model:

car = Car.arel_table
engine = Engine.arel_table

eng_exp = car.join(engine).on(car[:engine_id].eq(engine[:id]))
eng_exp.to_sql #=> GOOD! very nice!
Car.joins(eng_exp) #=> Breaks!!

Why this doesn't work is beyond me. I don't know exactly what is missing. But it's the closest thing to a solution I have now. If somebody could help me completing my example or provide me with a nice work-around or tell me when will Rails include such an obviously necessary feature will have my everlasting gratitude.

like image 626
ChuckE Avatar asked Feb 22 '13 17:02

ChuckE


People also ask

How is the left outer join symbol?

Left outer join (⟕) The left outer join is written as R ⟕ S where R and S are relations.

What does (+) mean in SQL joins?

The plus sign is Oracle syntax for an outer join. There isn't a minus operator for joins. An outer join means return all rows from one table. Also return the rows from the outer joined where there's a match on the join key. If there's no matching row, return null.

When left outer join is used?

A left outer join is a method of combining tables. The result includes unmatched rows from only the table that is specified before the LEFT OUTER JOIN clause. If you are joining two tables and want the result set to include unmatched rows from only one table, use a LEFT OUTER JOIN clause or a RIGHT OUTER JOIN clause.

What is left outer join in Oracle with example?

A LEFT OUTER JOIN performs an inner join of two tables (supposed table A which writes before the join keyword and table B which writes after the join keyword in the SQL statement ) based on the condition specified after the ON keyword. It returns all rows from the table A as well as the unmatched rows from the table B.


2 Answers

This is an old question, but for the benefit of anyone finding it through search engines:

If you want something you can pass into .joins, you can either use .create_join and .create_on:

join_on = car.create_on(car[:engine_id].eq(engine[:id]))
eng_join = car.create_join(engine, join_on, Arel::Nodes::OuterJoin)

Car.joins(eng_join)

OR

use the .join_sources from your constructed join object:

eng_exp = car.join(engine, Arel::Nodes::OuterJoin).on(car[:engine_id].eq(engine[:id]))
Car.joins(eng_exp.join_sources)
like image 177
DaveMongoose Avatar answered Sep 22 '22 10:09

DaveMongoose


I found a blog post that purports to address this problem: http://blog.donwilson.net/2011/11/constructing-a-less-than-simple-query-with-rails-and-arel/

Based on this (and my own testing), the following should work for your situation:

car = Car.arel_table
engine = Engine.arel_table   

sql = car.project(car[Arel.star])
        .join(engine, Arel::Nodes::OuterJoin).on(car[:engine_id].eq(engine[:id]))

Car.find_by_sql(sql)
like image 22
MrTheWalrus Avatar answered Sep 25 '22 10:09

MrTheWalrus