Rails: как увеличить нагрузку с помощью левой таблицы соединений? - PullRequest
1 голос
/ 17 мая 2019

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

products = Product.left_outer_joins(:productupdates).select("products.*, count(productupdates.*) as update_count, max(productupdates.old_price) as highest_price").group(:id)
products = products.paginate(:page => params[:page], :per_page => 20)

Этот запрос создает N + 1 запрос, но я не могу поставить .include (: productsupdates), так как у меня также есть оставленное соединение.

Если возможно, не могли бы вы мне помочь, как уменьшить количество запросов N + 1?

EDIT ------------------------------ Согласно предложению Вишала; Я изменил запрос контроллера следующим образом:

products = product.includes(:productupdates).select("products.*, count(productupdates.*) as productupdate_count, max(productupdates.old_price) as highest_price").group("productupdates.product_id")
products = products.paginate(:page => params[:page], :per_page => 20)

К сожалению, я получаю следующую ошибку:

ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "productupdates"
LINE 1: SELECT  products.*, count(productupdates.*) as productupdate_count, m...
                               ^
: SELECT  products.*, count(productupdates.*) as productupdate_count, max(productupdates.old_price) as highest_price FROM "products" WHERE "products"."isopen" = $1 AND (products.year > 2009) AND ("products"."make" IS NOT NULL) GROUP BY productupdates.product_id LIMIT $2 OFFSET $3):

Ответы [ 2 ]

2 голосов
/ 17 мая 2019

Пожалуйста, сообщите, как это вызывает N + 1 и как вы думаете, это решит проблему.Единственный способ увидеть ситуацию N + 1 - это если вы потом вызываете productupdates для каждого продукта.Если это так, то это не решит проблему.Пожалуйста, сообщите, чтобы другие могли сформулировать соответствующие ответы

В настоящее время я собираюсь предположить, что где-то позже в коде вы звоните productupdates по отдельным продуктам.Если это так, то мы можем решить это без агрегирования следующим образом:

@products = Product.eager_load(:productupdates) 

Теперь, когда мы зациклимся, обновления продукта уже загружены, так что, чтобы получить количество и максимум, мы можем сделать что-то вроде

@products.each do |p| 
  # COUNT 
  # (don't use the count method or it will execute a query )
  p.productupdates.size 
  # MAX old_price
  # older ruby versions use rails `try` instead 
  # e.g. p.productupdates.max_by(&:old_price).try(:old_price) || 0
  p.productupdates.max_by(&:old_price)&.old_price || 0
end

Использование этих методов не приведет к выполнению дополнительных запросов, поскольку обновления продукта уже загружены

Примечание: Причина, по которой includes не работает, заключается в том, что includes будетиспользовать 2 запроса для извлечения данных (внешнее соединение sudo), если не выполняется одно из следующих условий:

  • В предложении where используется условие поиска хеша, которое ссылается на таблицу ассоциации (например, where(productupdates: {old_price: 12}))
  • Вы включаете метод references (например, Product.includes(:productupdates).references(:productupdates))

В обоих этих случаях таблица будет оставлена ​​объединенной.В этом случае я решил использовать энергичную нагрузку в качестве includes делегатов на eager_load в вышеупомянутых случаях в любом случае

0 голосов
/ 17 мая 2019

Вы можете напрямую выполнить Product.includes(:productupdates), чтобы запросить базу данных с помощью левого внешнего соединения, а также преодолеть проблему запроса N + 1.

Поэтому вместо Product.left_outer_joins(:productupdates) в своем запросе используйте * 1005.*

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

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