我需要能夠使用ActiveRelation鏈接任意數量的子選擇UNION
。如何使用ActiveRelation編寫UNION鏈?
我對ARel的實現有點困惑,因爲它似乎假設UNION
是一個二進制操作。
但是:
(select_statement_a) UNION (select_statement_b) UNION (select_statement_c)
是有效的SQL。這可能沒有做不好的字符串替換?
我需要能夠使用ActiveRelation鏈接任意數量的子選擇UNION
。如何使用ActiveRelation編寫UNION鏈?
我對ARel的實現有點困惑,因爲它似乎假設UNION
是一個二進制操作。
但是:
(select_statement_a) UNION (select_statement_b) UNION (select_statement_c)
是有效的SQL。這可能沒有做不好的字符串替換?
雖然他在正確的軌道上,但你可以做得比Adam Lassek提出的要好一些。我剛剛解決了一個類似的問題,試圖從社交網絡模型中獲得朋友列表。朋友可以通過各種方式自動獲取,但我希望有一個ActiveRelation友好的查詢方法,可以處理進一步的鏈接。所以我有
class User
has_many :events_as_owner, :class_name => "Event", :inverse_of => :owner, :foreign_key => :owner_id, :dependent => :destroy
has_many :events_as_guest, :through => :invitations, :source => :event
def friends
friends_as_guests = User.joins{events_as_guest}.where{events_as_guest.owner_id==my{id}}
friends_as_hosts = User.joins{events_as_owner}.joins{invitations}.where{invitations.user_id==my{id}}
User.where do
(id.in friends_as_guests.select{id}
) |
(id.in friends_as_hosts.select{id}
)
end
end
end
它利用Squeels子查詢支持。生成的SQL是
SELECT "users".*
FROM "users"
WHERE (("users"."id" IN (SELECT "users"."id"
FROM "users"
INNER JOIN "invitations"
ON "invitations"."user_id" = "users"."id"
INNER JOIN "events"
ON "events"."id" = "invitations"."event_id"
WHERE "events"."owner_id" = 87)
OR "users"."id" IN (SELECT "users"."id"
FROM "users"
INNER JOIN "events"
ON "events"."owner_id" = "users"."id"
INNER JOIN "invitations"
ON "invitations"."user_id" =
"users"."id"
WHERE "invitations"."user_id" = 87)))
在您需要的組件數量可變的證明有輕微的修改上面的代碼
def friends
friends_as_guests = User.joins{events_as_guest}.where{events_as_guest.owner_id==my{id}}
friends_as_hosts = User.joins{events_as_owner}.joins{invitations}.where{invitations.user_id==my{id}}
components = [friends_as_guests, friends_as_hosts]
User.where do
components = components.map { |c| id.in c.select{id} }
components.inject do |s, i|
s | i
end
end
end
這裏是一個粗略的猜測,對解決方案的另一種模式OP的具體問題
class Shift < ActiveRecord::Base
def self.limit_per_day(options = {})
options[:start] ||= Date.today
options[:stop] ||= Date.today.next_month
options[:per_day] ||= 5
queries = (options[:start]..options[:stop]).map do |day|
where{|s| s.scheduled_start >= day}.
where{|s| s.scheduled_start < day.tomorrow}.
limit(options[:per_day])
end
where do
queries.map { |c| id.in c.select{id} }.inject do |s, i|
s | i
end
end
end
end
由於ARel訪問者生成工會的方式,在使用Arel::Nodes::Union
時,我不斷收到SQL錯誤。看起來老式的字符串插值是實現這種工作的唯一方法。
我有一個Shift模型,我想獲得給定日期範圍內的班次集合,每天限制爲五班倒。這是對變速模型類方法:
def limit_per_day(options = {})
options[:start] ||= Date.today
options[:stop] ||= Date.today.next_month
options[:per_day] ||= 5
queries = (options[:start]..options[:stop]).map do |day|
select{id}.
where{|s| s.scheduled_start >= day}.
where{|s| s.scheduled_start < day.tomorrow}.
limit(options[:per_day])
end.map{|q| "(#{ q.to_sql })" }
where %{"shifts"."id" in (#{queries.join(' UNION ')})}
end
(我用Squeel除了ActiveRecord的)
不必訴諸字符串插值很討厭,但至少用戶提供的參數正確消毒。我當然會很感激提出這個更清潔的建議。
有一種方法使用AREL,使這項工作:
tc=TestColumn.arel_table
return TestColumn.where(tc[:id]
.in(TestColumn.select(:id)
.where(:attr1=>true)
.union(TestColumn.select(:id)
.select(:id)
.where(:attr2=>true))))
我不認爲你瞭解這個問題。這是兩個查詢之間的單個UNION,工作正常。我需要能夠將UNION鏈接到任意長度。 UNION語義在SQL實現中可能不同 - 在這種情況下,我使用的是PostgreSQL。 –
我做過了,這是最接近我使用arel來執行聯合的。 –
無論如何,你可以通過執行'Domain.where(attr => value).union(Domain.where(attr2 => value))'''也許你可以在'to_sql'方法之後得到一個Arel :: Node,並將它傳遞給'find_by_sql' –
我喜歡Squeel。但不要使用它。所以我來到這個解決方案(Arel 4.0.2)
def build_union(left, right)
if right.length > 1
Arel::Nodes::UnionAll.new(left, build_union(right[0], right[1..-1]))
else
Arel::Nodes::UnionAll.new(left, right[0])
end
end
managers = [select_manager_1, select_manager_2, select_manager_3]
build_union(managers[0], managers[1..-1]).to_sql
# => ((SELECT table1.* from table1)
# UNION ALL
# ((SELECT table2.* from table2)
# UNION ALL
# (SELECT table3.* from table3)))
看來他們爲非二元工會添加了'Arel :: Nodes :: UnionAll'。當我可以使用Arel 4時,我會再次訪問。 –
非常感謝!我正在尋找這個。 –
但是,這是一個稍微不同的情況。我需要能夠鏈接任意數量的選擇。所以我仍然不得不求助於字符串插值,而不管我是使用'UNION'還是'OR'。除非你可以用Squeel/ARel建議一種方法來實現這一點? –
這可以做到。它只需要一點點的魔力。讓我想想。可能可以使用Enumerable注入來完成。如果它有效,我會給它一個更新的答案。 – bradgonesurfing
好的。我做了一個等同的功能,首先將組件放入數組中,然後組合這些組件。請記住,SQueel僅僅是一些缺乏魔法的方法而已。您可以在where塊中執行普通ruby,並動態構建查詢。 – bradgonesurfing