Активная запись.включает в себя предложение where

#ruby-on-rails

#ruby-on-rails

Вопрос:

Я пытаюсь избежать запроса N 1 с помощью includes , но мне нужно отфильтровать некоторые дочерние записи. Вот что у меня есть до сих пор:

 Column.includes(:tickets).where(board_id: 1, tickets: {sprint_id: 10})
 

Проблема в том, что возвращаются только столбцы, содержащие билеты с sprint_id числом 10. Я хочу вернуть все столбцы с board_id равным 1 и предварительно извлекать билеты только с sprint_id числом 10, так что column.tickets это либо пустой список списка объектов билетов с sprint_id числом 10.

Комментарии:

1. Не имеет смысла реализовывать то, что вы хотите, в одном операторе. По крайней мере, для меня. Я бы сделал это как таковой, сначала удалил билеты из инструкции where. Во-вторых, когда вы вызываете column.tickets add в конце .where(sprint_id: 10) . Кроме этого, я бы, вероятно, написал пользовательский sql.

2. Понял. Проблема в том, что это создает запрос N 1, где N — количество столбцов, нет?

3. Если это произойдет, попробуйте добавить Column.all ?

4. Не верьте, что эти запросы должны вызывать N 1: @columns = Column.includes(:tickets).where(board_id: 1) а затем @columns или @columns.tickets.where(sprint_id: 10)

5. Кажется, вызывает N 1 для меня в консоли rails. Это должно было бы выполнять where предложение с использованием кода Ruby в памяти вместо выполнения SQL-запроса, но я вижу, что он выводится SELECT ... FROM tickets WHERE sprint_id = 10 для каждой итерации.

Ответ №1:

Именно так должен работать includes . Когда вы добавляете предложение where, оно применяется ко всему запросу, а не только к загрузке связанных записей.

Один из способов сделать это — перевернуть запрос назад:

 columns = Ticket.eager_load(:columns)
                .where(sprint_id: 10, columns: { board_id: 1 })
                .map(amp;:column)
                .uniq
 

Ответ №2:

Column.includes(:tickets).where(board_id: 1, tickets: {sprint_id: 10}) выполняет два SQL-запроса. Один для выбора столбцов, соответствующих указанному предложению where, а другой для выбора и загрузки билетов, которые column_id равны идентификатору соответствующих столбцов.

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

 columns = Column.where(board_id: 1).to_a
tickets = Ticket.where(column_id: columns.map(amp;:id), sprint_id: 10).to_a
 

Таким образом, вы не сможете вызывать #tickets каждый столбец (так как он снова выполнит запрос к базе данных, и у вас возникнет проблема N 1), но чтобы иметь аналогичный способ доступа к билетам столбца без выполнения каких-либо запросов, вы можете сделать что-то вроде этого:

 grouped_tickets = tickets.group_by(amp;:column_id)
columns.each do |column|
  column_tickets = grouped_tickets[column.id]
  # Do something with column_tickets if they're present.
end
 

Комментарии:

1. Будьте осторожны с этими to_a вызовами, загружать все в память, не зная количества записей, — плохая практика. Используется Column.where(board_id: 1).pluck(:id) для получения идентификаторов более производительным способом (позже может потребоваться еще один SQL-запрос для перебора записей, но это должно оказать большое влияние)