Say I have two relations that hold records in the same model, such as:
@companies1 = Company.where(...)
@companies2 = Company.where(...)
How can I find the intersection of these two relations, i.e. only those companies that exist within both?
In set theory, the intersection of a collection of sets is the set that contains their shared elements. Given two sets, A = {2, 3, 4, 7, 10} and B = {1, 3, 5, 7, 9}, their intersection is as follows: The intersection of two sets is commonly represented using a Venn diagram. In a Venn diagram, a set is represented by a circle.
In set theory, the intersection of a collection of sets is the set that contains their shared elements. Given two sets, A = {2, 3, 4, 7, 10} and B = {1, 3, 5, 7, 9}, their intersection is as follows:
Prove that the intersection of two equivalence relations is an equivalence relation. - Mathematics Stack Exchange Bookmark this question. Show activity on this post. Closed. This question does not meet Mathematics Stack Exchange guidelines.
The intersection of A and B, A ∩ B, is shaded in red. Its complement, (A ∩ B) C is shaded in grey. The union of the complements of A and B, A C ∪ B C, is also shaded in grey. The complement of the union of two sets is equal to the intersection of their complements:
I solve similar problem this way
Company.connection.unprepared_statement do
Company.find_by_sql "#{@companies1.to_sql} INTERSECT #{@companies2.to_sql}"
end
We need unprepared_statement
block here because latest Rails versions use prepared statements to speed up arel queries, but we need pure SQL in place.
By default connecting those where
together creates AND which is what you want.
So many be:
class Company < ActiveRecord::Base
def self.where_1
where(...)
end
def self.where_2
where(...)
end
end
@companies = Company.where_1.where_2
====== UPDATED ======
There are two cases:
# case 1: the fields selecting are different
Company.where(:id => [1, 2, 3, 4]) & Company.where(:other_field => true)
# a-rel supports &, |, +, -, but please notice case 2
# case 2
Company.where(:id => [1, 2, 3]) & Company.where(:id => [1, 2, 4, 5])
# the result would be the same as
Company.where(:id => [1, 2, 4, 5])
# because it is &-ing the :id key, instead of the content inside :id key
So if you are in case 2, you will need to do like what @apneadiving commented.
Company.where(...).all & Company.where(...).all
Of course, doing this sends out two queries and most likely queried more results than you needed.
Use sql keyword INTERSECT.
params1 = [1,2,4]
params2 = [1,3,4]
query = "
SELECT companies.* FROM companies
WHERE id in (?,?,?)
INTERSECT
SELECT companies.* FROM companies
WHERE id in (?,?,?)
"
Company.find_by_sql([query, *params1, *params2])
it will be faster than previous solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With