Оптимизация - Rails belongs_to имеет проблему n+1, как сделать чтоб в sql был join?

Имею записи логов, у них есть отсылка на пользователя.

Модель логов

class Syslog < ApplicationRecord
 belongs_to :user
end

Модель пользователя ничего особенного не имеет, has_many я не добавлял там.

Теперь когда я делаю Syslog.all

А в представлении обращаюсь к пользователю:

    <% @syslogs.each do |syslog| %>
      <tr>
        <td><%= syslog.datetime %></td>
        <td><%= syslog.user.username %></td>
      </tr>
    <% end %>

В логах я вижу, что делается множество повторяющихся запросов.

  Syslog Load (1.2ms)  SELECT `SYSLOG`.* FROM `SYSLOG` INNER JOIN `USERS` ON `USERS`.`I_ID` = `SYSLOG`.`I_UID` WHERE (I_ELCAT <= '3') LIMIT 50
  ↳ app/views/syslogs/index.html.erb:13
  User Load (1.4ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  User Load (1.1ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 2 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  User Load (1.3ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -20 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  User Load (2.2ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  User Load (1.2ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -11 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -11 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  User Load (1.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -17 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -11 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.1ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -1 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  User Load (2.1ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -15 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -20 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  User Load (1.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -12 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  User Load (1.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -13 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -13 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -13 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -13 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -13 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  CACHE User Load (0.0ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -13 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  User Load (0.8ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = -18 LIMIT 1
  ↳ app/views/syslogs/index.html.erb:16
  Rendered syslogs/index.html.erb within layouts/application (Duration: 163.4ms | Allocations: 64237)
  User Load (1.3ms)  SELECT `USERS`.* FROM `USERS` WHERE `USERS`.`I_ID` = 11 LIMIT 1

Получается технически сначала выбираются все логи. А потом в цикле для каждого лога выбирается пользователь.

Мне надо сделать это одним или минимальным количеством запросов. Как этого добиться?


Ответы (1 шт):

Автор решения: Василиса

Это не надо делать через join, в ActiveRecord есть разные варианты прелоада для этого

@syslogs = Syslog.includes(:user).all
→ Ссылка