Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Intersection of two relations

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?

like image 389
sscirrus Avatar asked Jun 22 '11 05:06

sscirrus


People also ask

How do you find the intersection of two sets?

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.

What is the intersection of a collection of sets?

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:

Is the intersection of two equivalence relations an equivalence relation?

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.

What is the intersection of a and B in math?

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:


3 Answers

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.

like image 157
mikdiet Avatar answered Oct 21 '22 09:10

mikdiet


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.

like image 12
PeterWong Avatar answered Oct 21 '22 09:10

PeterWong


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.

like image 7
Michael Avatar answered Oct 21 '22 10:10

Michael