Быстрый сложный запрос для выбора бронирований - PullRequest
2 голосов
/ 28 октября 2010

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

У нас уже есть рабочий отчет, но он использует несколько запросов для получения необходимой информации. Один, чтобы получить курсы, один, чтобы получить заказы, и один, чтобы получить количество посетителей. Это очень медленно из-за размера базы данных.

Существует ряд дополнительных условий для отчетов:

  • Бронирование должно быть сделано более 5 минут назад или были подтверждены
  • Бронирование не должно быть отменено
  • Курс не должен быть отмечен как удаленный
  • Место и место проведения курсов должно быть LIKE строка поиска
  • Курсы без бронирований должны появляться в результатах

Это структура таблицы: (я опустил ненужную информацию. Все поля не равны NULL и не имеют значений по умолчанию)

mysql> ОПИСАТЬ first_aid_courses;

+------------------+--------------+-----+----------------+
| Field            | Type         | Key | Extra          |
+------------------+--------------+-----+----------------+
| id               | int(11)      | PRI | auto_increment |
| course_date      | date         |     |                |
| region_id        | int(11)      |     |                |
| location         | varchar(255) |     |                |
| venue            | varchar(255) |     |                |
| number_of_spaces | int(11)      |     |                |
| deleted          | tinyint(1)   |     |                |
+------------------+--------------+-----+----------------+

mysql> ОПИСАНИЕ first_aid_bookings;

+-----------------------+--------------+-----+----------------+
| Field                 | Type         | Key | Extra          |
+-----------------------+--------------+-----+----------------+
| id                    | int(11)      | PRI | auto_increment |
| first_aid_course_id   | int(11)      |     |                |
| placed                | datetime     |     |                |
| confirmed             | tinyint(1)   |     |                |
| cancelled             | tinyint(1)   |     |                |
+-----------------------+--------------+-----+----------------+

mysql> ОПИСАНИЕ first_aid_attendees;

+----------------------+--------------+-----+----------------+
| Field                | Type         | Key | Extra          |
+----------------------+--------------+-----+----------------+
| id                   | int(11)      | PRI | auto_increment |
| first_aid_booking_id | int(11)      |     |                |
+----------------------+--------------+-----+----------------+

mysql> ОПИСАТЬ регионы;

+----------+--------------+-----+----------------+
| Field    | Type         | Key | Extra          |
+----------+--------------+-----+----------------+
| id       | int(11)      | PRI | auto_increment |
| name     | varchar(255) |     |                |
+----------+--------------+-----+----------------+

Мне нужно выбрать следующее:

Course ID:        first_aid_courses.id
Date:             first_aid_courses.course_date
Region            regions.name
Location:         first_aid_courses.location
Bookings:         COUNT(first_aid_bookings)
Attendees:        COUNT(first_aid_attendees)
Spaces Remaining: COUNT(first_aid_bookings) - COUNT(first_aid_attendees)

Это то, что я имею до сих пор:

SELECT `first_aid_courses`.*,
       COUNT(`first_aid_bookings`.`id`)  AS `bookings`,
       COUNT(`first_aid_attendees`.`id`) AS `attendees`
FROM   `first_aid_courses`
       LEFT JOIN `first_aid_bookings`
         ON `first_aid_courses`.`id` =
            `first_aid_bookings`.`first_aid_course_id`
       LEFT JOIN `first_aid_attendees`
         ON `first_aid_bookings`.`id` =
            `first_aid_attendees`.`first_aid_booking_id`
WHERE  ( `first_aid_courses`.`location` LIKE '%$search_string%'
          OR `first_aid_courses`.`venue` LIKE '%$search_string%' )
       AND `first_aid_courses`.`deleted` = 0
       AND ( `first_aid_bookings`.`placed` > '$five_minutes_ago'
             AND `first_aid_bookings`.`cancelled` = 0
              OR `first_aid_bookings`.`confirmed` = 1 )
GROUP  BY `first_aid_courses`.`id`
ORDER  BY `course_date` DESC  

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

Ответы [ 3 ]

2 голосов
/ 28 октября 2010

Хорошо, я ответил на свой вопрос. Иногда это помогает задать вопрос, чтобы вы выяснили ответ.

SELECT `first_aid_courses`.*,
       `regions`.`name`                          AS `region_name`,
       COUNT(DISTINCT `first_aid_bookings`.`id`) AS `bookings`,
       COUNT(`first_aid_attendees`.`id`)         AS `attendees`
FROM   `first_aid_courses`
       JOIN `regions`
         ON `first_aid_courses`.`region_id` = `regions`.`id`
       LEFT JOIN `first_aid_bookings`
         ON `first_aid_courses`.`id` =
            `first_aid_bookings`.`first_aid_course_id`
       LEFT JOIN `first_aid_attendees`
         ON `first_aid_bookings`.`id` =
            `first_aid_attendees`.`first_aid_booking_id`
WHERE  ( `first_aid_courses`.`location` LIKE '%$search_string%'
          OR `first_aid_courses`.`venue` LIKE '%$search_string%' )
       AND `first_aid_courses`.`deleted` = 0
       AND ( `first_aid_bookings`.`cancelled` = 0
             AND `first_aid_bookings`.`confirmed` = 1 )
GROUP  BY `first_aid_courses`.`id`
ORDER  BY `course_date` ASC  
1 голос
/ 28 октября 2010

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

Еще одна важная производительностьэто как LIKE "% nnn%".

Можно ли что-то с ними сделать?

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

У меня есть запросы, подобные LIKE для таблиц с миллионами строк.Это не проблема, если остальная часть запроса может устранить любые ненужные соответствия.

Вы можете использовать подзапросы, чтобы уменьшить область действия для запросов LIKE.

1 голос
/ 28 октября 2010

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

SUM(IF(`first_aid_bookings`.`id` IS NOT NULL, 1, 0))  AS `bookings`,
COUNT(IF(`first_aid_attendees`.`id` IS NOT NULL, 1, 0)) AS `attendees`
...