Пересечение двух отношений - PullRequest
10 голосов
/ 22 июня 2011

Скажем, у меня есть два отношения, которые содержат записи в одной и той же модели, например:

@companies1 = Company.where(...)
@companies2 = Company.where(...)

Как я могу найти пересечение этих двух отношений, то есть только тех компаний, которые существуют в обоих?

Ответы [ 5 ]

12 голосов
/ 22 июня 2011

По умолчанию при соединении этих where создается AND, что вам и нужно.

Так много будет:

class Company < ActiveRecord::Base
  def self.where_1
    where(...)
  end
  def self.where_2
    where(...)
  end
end

@companies = Company.where_1.where_2

====== ОБНОВЛЕНО ======

Есть два случая:

# 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

Так что, если вы находитесь в случае 2, вам нужно поступить так, как прокомментировал @apneadiving.

Company.where(...).all & Company.where(...).all

Конечно, при этом отправляются два запроса, и, скорее всего, запрашивается больше результатов, чем вам нужно.

5 голосов
/ 02 июля 2012

Используйте ключевое слово sql 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])

это будет быстрее, чем предыдущее решение.

4 голосов
/ 28 августа 2013

Я решаю подобную проблему следующим образом

Company.connection.unprepared_statement do
  Company.find_by_sql "#{@companies1.to_sql} INTERSECT #{@companies2.to_sql}"
end

Нам нужен блок unprepared_statement здесь, потому что последние версии Rails используют подготовленные операторы для ускорения запросов arel, но нам нужен чистый SQL на месте.

1 голос
/ 07 октября 2015

Вы можете использовать ActiveRecord::SpawnMethods#merge

Пример:

Company.where(condition: 'value').merge(Company.where(other_condition: 'value'))
0 голосов
/ 17 апреля 2018

Для тех, кто застрял с Rails4 и не может использовать Rails5. Или синтаксис:

У меня было динамически большое количество больших запросов, которые имели схожие условия (и, следовательно, также схожие результаты). У моего грабли-сервера возникли бы проблемы, когда все они были бы одновременно созданы, преобразованы в массивы и затем объединены.

Мне нужен ActiveRecord :: Relation (еще не запущен) для использования с find_each.

Выглядело примерно так:

Class Conditions
  def initialize
    self.where_arr = []
    self.joins_arr = []
  end

  def my_condition1
    where_arr << 'customers.status = "active"'
    joins_arr << :customer
  end

  def my_condition2
    where_arr << 'companies.id = 1'
  end
end

conditions = []    
joins = []
# probably call them in a .each block with .send
conditions1 = Conditions.new
conditions1.my_condition1
conditions1.my_condition2
conditions << "(#{conditions1.where_arr.join(' AND ')})"
joins << conditions1.joins_arr

conditions2 = Conditions.new
conditions2.my_condition1
joins << conditions2.joins_arr

Company.joins(joins).where(conditions.join(' OR '))

=> SELECT companies.* FROM companies 
INNER JOIN customers ON companies.customer_id = customers.id 
WHERE (customers.status = 'active' AND companies.id = 1) OR
(customers.status = 'active')

Это вроде хакерский, но он работает, пока вы, возможно, не сможете перейти на Rails 5

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...