我正在尝试避免在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_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)
有没有办法以我想要的方式做到这一点?
答案 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)