2016-03-18 3 views
2

Я в недоумении, как реализовать следующую спецификацию, не прибегая к отображению/выбору и тому подобное, используя Ruby и стараюсь как можно больше использовать SQL.Запрос ActiveRecord в Rails

У меня есть «компании», у которых много «служащих».

Один из сотрудников компании отмечен как «контактная точка» (булево поле). Отображается в поле contact_points.

Компании также имеют множество «заказов».

Задача состоит в том, чтобы запросить всех сотрудников контактной станции в течение определенного периода времени и вернуть тех, кто компания разместила свой первый заказ в течение периода.

Так что это комбинация поиска минимального created_at времени для заказов, сгруппированных по company_id, а затем вернитесь к компании, чтобы найти сотрудника контактной точки. (Я думаю).

После многих издевательств и пыхтывания, пытаясь использовать чистые решения ActiveRecord/SQL, я прибегал к этому.

Employee 
    .contact_points 
    .joins(:company) 
    .where(
    companies: { 
     id: Order.all.group_by(&:company_id) 
      .map{ |o| o.last.sort{ |a,b| a.created_at <=> b.created_at }.first } 
      .keep_if{ |o| (date_from..date_to).cover?(o.created_at) } 
      .map(&:company_id) 
    } 
) 

date_from и date_to очищаются даты Params из формы отчета.

Я надеюсь, что может быть способ упростить это?

ответ

1

Ваш текущий подход хлебает все заказов в память , который будет медленным и аварийным. Я хотел бы сделать это следующим образом:

Employee 
    .contact_points 
    .joins(:company) 
    .where(<<-EOQ, date_from, date_to, date_from) 
    EXISTS (SELECT 1 
      FROM orders o 
      WHERE o.company_id = companies.id 
      AND  o.created_at BETWEEN ? AND ?) 
    AND NOT EXISTS (SELECT 1 
        FROM orders o 
        WHERE o.company_id = companies.id 
        AND  o.created_at < ?) 
    EOQ 

В самом деле, если нет других условий, вам даже не нужно, чтобы присоединиться к companies, потому что вы могли бы просто использовать employees.company_id.

EDIT: Или это может быть более производительным:

Employee 
    .contact_points 
    .joins(<<-EOQ) 
    INNER JOIN (SELECT o.company_id, MIN(o.created_at) created_at 
       FROM orders o 
       GROUP BY company_id) min_orders 
    ON employees.company_id = min_orders.company_id 
    EOQ 
    .where("min_orders.created_at BETWEEN ? AND ?", date_from, date_to) 
+0

Пол, спасибо за это, я использовал второй пример, и это работает! Я ценю помощь. –

Смежные вопросы