2015-03-09 1 views
1

Я пытаюсь улучшить SQL для следующего графика:Улучшение планирования/бронирования SQL запрос в рельсах

enter image description here

Это выглядит примерно так:

- @users.each do |user| 
    - @dates.each do |date| 
    %td 
     - Booking.where(user: user, date: date).each do |booking| 
     = booking.shift_time 

@users состоит из пользователей присутствующих в таблице.

@dates состоит из дат: начало недели -> конец недели

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

Как я могу улучшить этот запрос? Возможно ли даже получить все эти данные из базы данных, используя один (большой) запрос? Я использую PostgreSQL, если это имеет значение.

Одно из предложений заключается в том, что я получаю все заказы, которые находятся между @dates и группируются по датам. Но это означает, что мне придется заменить несуществующие заказы на определенную дату и пользователя значением NULL, но я не знаю, как я смогу это сделать.

Вот выход SQL:

SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 1 AND "bookings"."date" = '2015-03-09' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 1 AND "bookings"."date" = '2015-03-10' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 1 AND "bookings"."date" = '2015-03-11' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 1 AND "bookings"."date" = '2015-03-12' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 1 AND "bookings"."date" = '2015-03-13' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 1 AND "bookings"."date" = '2015-03-14' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 1 AND "bookings"."date" = '2015-03-15' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 2 AND "bookings"."date" = '2015-03-09' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 2 AND "bookings"."date" = '2015-03-10' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 2 AND "bookings"."date" = '2015-03-11' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 2 AND "bookings"."date" = '2015-03-12' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 2 AND "bookings"."date" = '2015-03-13' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 2 AND "bookings"."date" = '2015-03-14' 
SELECT "bookings".* FROM "bookings" WHERE "bookings"."user_id" = 2 AND "bookings"."date" = '2015-03-15' 
+0

Вы понимаете, что делаете запросы users.count * dates.count вместо одного запроса (если только вы не сделали специальные измерения, о которых я знаю, вы этого не сделали, учитывая вопрос). – iced

+0

@iced Да, я знаю. Вот почему я прошу о помощи в том, как улучшить это. – karlingen

ответ

1

можно сделать следующим образом:

# controller 
def your_action 
    @users = User.some_scope 
    @dates = some_logic_to_return_array_of_dates 
    bookings = Booking.where(user_id: @users.map(&:id), date: @dates) 
    # the above will select all Booking record having user_id in the selected users' ids AND date IN the selected range/array of dates 
    @bookings_by_date = bookings.group_by(&:date) 
    # group results like 
    # { 
    # <Date> => [<Booking>, <Booking>], 
    # <Date2> => [<Booking>] 
    # } 
end 

Тогда на ваш взгляд:

# view 
- @users.each do |user| 
    - @dates.each do |date| 
    %td 
     - todays_bookings = @bookings_by_date[date].presence || [] 
     - todays_bookings.select{ |booking| booking.user == user }.each do |booking| 
     = booking.shift_time 
+0

Замечательный. От 5 секунд до 0,7 секунды. Удивительно! Огромное спасибо – karlingen

0
# action 
@dates = start_date..end_date 
@users = User.some_scope.includes(:bookings) 

# view 
- @users.each do |user| 
    %tr 
    - @dates.each do |date| 
     %td 
     = user.bookings.select{|booking| booking.date == date}.each do |user_booking| 
      = user_booking 

Это должен попасть только базы данных дважды (2 запроса для пользователей a nd one для заказов)

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