#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-запрос для перебора записей, но это должно оказать большое влияние)