Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using `joins()` for first an INNER JOIN then a LEFT JOIN of the next table

I am trying to avoid string interpolating my joins in Rails because I've noticed a decrease in flexibility when chaining queriers together. That is, I feel that joins(:table1) is much more flexible than joins('inner join table1 on table1.id = this_table.table1_id').

What I would like to accomplish is:

FROM table1
INNER JOIN table2 on table2.id = table1.table2_id
LEFT JOIN table3 on table3.id = table2.table3_id

inner join all

However, I can't figure out how to do it using Rails parlance:

Table1.joins(table2: :table3)

Results in an INNER join on the final table.

FROM table1
INNER JOIN table2 on table2.id = table1.table2_id
INNER JOIN table3 on table3.id = table2.table3_id

left joins all

If I use left_outer_joins...

Table1.left_joins(table2: :table3)

Results in LEFT joins on both tables (unwanted).

FROM table1
LEFT JOIN table2 on table2.id = table1.table2_id
LEFT JOIN table3 on table3.id = table2.table3_id

how to mix joins ?

Can't seem to chain joins without specify the relations ... that is, these don't work:

Table1.joins(:table2).left_join(table2: :table3)
Table1.joins(:table2).left_join(:table3)

Is there any way to do this the way I want?

like image 921
thornomad Avatar asked Feb 27 '18 20:02

thornomad


People also ask

When to use inner join and left join?

You'll use INNER JOIN when you want to return only records having pair on both sides, and you'll use LEFT JOIN when you need all records from the “left” table, no matter if they have pair in the “right” table or not.

Is it possible for left join and inner join to produce the same results?

The reason why LEFT JOIN and INNER JOIN results are the same is because all the records of table branch has at least one match on table user_mast . The main difference between INNER JOIN and LEFT JOIN is that LEFT JOIN still displays the records on the the LEFT side even if they have no match on the RIGHT side table.

How do I join two tables with left join in SQL?

Syntax For Left Join:SELECT column names FROM table1 LEFT JOIN table2 ON table1. matching_column = table2. matching_column; Note: For example, if you have a left table with 10 rows, you are guaranteed to have at least 10 rows after applying join operation on two tables.


3 Answers

Here are 3 options, but the 1st option should be the best.

Option #1:
You can reorder your Joins as below, I have the same scenario and I tried the below and it worked. What you are doing here is using the Table2 at the beginning, so it can be join with Table1 and can be left_outer_join with Table3:

Table2.joins(:table1).left_outer_joins(:table3)

Option #2:
You can use Raw Sql in Rails as below:

Table1.joins(:table2).joins('LEFT OUTER JOIN table3  ON  table3.id = table2.table3_id')

Option #3:
Another way to get the data of Table3 data, is to use includes as below, but it will fire 3 queries:

Table1.joins(:table2).includes(table2: :table3)
like image 52
Shiko Avatar answered Oct 17 '22 08:10

Shiko


Table1.joins(:table2).left_joins(table2: :table3) work for me. (Using Rails 6.0.3.2 & Postgres)

like image 34
Metal Avatar answered Oct 17 '22 09:10

Metal


As far as I know, there is not a "user-facing" syntax to do so with #left_outer_joins (and I wasn't able to get it working using #merge either), but you can achieve it as follows:

left_join_table3 = Table1
  .left_joins(:table2 => :table3)
  .arel.join_sources[1]

Table1.joins(:table2, left_join_table3)

# Generates the following SQL (for belongs_to associations):
#
# SELECT "table1".* FROM "table1"
# INNER JOIN "table2" ON "table2"."id" = "table1"."table2_id"
# LEFT OUTER JOIN "table3" ON "table3"."id" = "table2"."table3_id"

For re-use, i.e., in a scope, if you leave out the constant, I believe that the aliases will be consistent, which it sounds like you are trying to achieve.

scope :my_scope, -> {
  left_join_table3 = left_joins(:table2 => :table3).arel.join_sources[1]
  joins(:table2, left_join_table3)
}
like image 1
Steve Avatar answered Oct 17 '22 10:10

Steve