首先使用`join()`作为INNER JOIN,然后使用下一个表的LEFT JOIN

时间:2018-02-27 20:40:48

标签: ruby-on-rails ruby-on-rails-5

我正在尝试避免在Rails中对joins进行字符串插值,因为我注意到将查询器链接在一起时灵活性会降低。  也就是说,我认为joins(:table1)joins('inner join table1 on table1.id = this_table.table1_id')更灵活。

我想要完成的是:

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

内连接全部

但是,我无法弄清楚如何使用Rails用语:

Table1.joins(table2: :table3)

在决赛桌上进行INNER联接。

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

left加入所有

如果我使用left_outer_joins ...

Table1.left_joins(table2: :table3)

两个表的LEFT连接结果(不需要的)。

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

如何混合连接?

似乎没有指定关系链接连接......也就是说,这些不起作用:

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

有没有办法以我想要的方式做到这一点?

4 个答案:

答案 0 :(得分:3)

这里有3个选项,但第一个选项应该是最好的。

选项#1:
您可以重新排序加入,如下所示,我有相同的方案,我尝试了下面的工作。你在这里做的是在开头使用Table2,因此它可以加入 Table1 ,并且可以 left_outer_join Table3

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

选项#2:
您可以在Rails中使用 Raw Sql ,如下所示:

includes

选项#3:
获取 Table3 数据数据的另一种方法是使用Table1.joins(:table2).includes(table2: :table3) ,如下所示,但它会触发3个查询:

#standardSQL
SELECT 
  id, 
  slice,
  pos,
  value
FROM `project.dataset.messytable` t,
UNNEST(REGEXP_EXTRACT_ALL(TO_JSON_STRING(t), r'"x__x_arrVal_arrSlices_(\d+)":\[.*?\]')) slice WITH OFFSET x
JOIN UNNEST(REGEXP_EXTRACT_ALL(TO_JSON_STRING(t), r'"x__x_arrVal_arrSlices_\d+":\[(.*?)\]')) arr WITH OFFSET y
ON x = y,
UNNEST(SPLIT(arr)) value WITH OFFSET pos   

答案 1 :(得分:2)

Table1.joins(:table2).left_joins(table2: :table3)为我工作。 (使用Rails 6.0.3.2和Postgres)

答案 2 :(得分:0)

据我所知,#left_outer_joins并没有“面向用户”的语法(而且我也无法使用#merge使它运行),但是您可以达到以下目的:

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"

对于重用,即在范围内,如果您省略常量,我相信别名将是一致的,这听起来像是您要实现的。

scope :my_scope, -> {
  left_join_table3 = left_joins(:table2 => :table3).arel.join_sources[1]
  joins(:table2, left_join_table3)
}

答案 3 :(得分:0)

这不是最干净的方法,但我通过向 ActiveRecord 添加一个名为 join_sql 的方法来解决这个问题。

class ActiveRecord::Base
  def self.join_sql(scope=current_scope)
    sql = scope.to_sql
    join_index = sql.index(/(LEFT OUTER|INNER JOIN)/)
    if join_index
      join_end_index = sql.index(/ (WHERE|GROUP BY|ORDER BY)/) || -1
      sql[join_index..join_end_index]
    end
  end
end

然后,您可以使用带有字符串而不是符号的 ActiveRecord joins 方法:

Table1.joins(:table2).joins(Table2.left_joins(:table3).join_sql)