选择预订的快速复杂查询

Posted

技术标签:

【中文标题】选择预订的快速复杂查询【英文标题】:Fast complex query to select bookings 【发布时间】:2010-10-27 21:56:12 【问题描述】:

我正在尝试编写查询以获取课程信息以及预订和参加者的数量。每个课程可以有多个预订,每个预订可以有多个参加者。

我们已经有了一份工作报告,但它使用多个查询来获取所需的信息。一份获取课程,一份获取预订,一份获取参加人数。由于数据库已经增长到很大,这非常慢。

报告有许多额外的条件:

预订必须超过 5 几分钟前,或已确认 不得取消预订 不得将课程标记为已删除 课程场地和地点必须是LIKE 搜索字符串 没有预订的课程必须出现在结果中

这是表结构:(我省略了不需要的信息。所有字段都不为空且没有默认值)

mysql> DESCRIBE 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> DESCRIBE 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> DESCRIBE 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  

它不太好用,谁能帮我写出正确的查询?此数据库中还有 1000 行,因此任何关于使其快速的帮助都将受到赞赏(例如要索引的字段)。

【问题讨论】:

它“不太好用”是怎么回事? 预订/参加人数错误 【参考方案1】:

好的,我已经回答了我自己的问题。有时问一个问题让你找出答案会有所帮助。

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  

【讨论】:

【参考方案2】:

这完全未经测试,但可以尝试为预订和参加者选择非空行数,如下所示:

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`

【讨论】:

我无法让它工作。它返回与我的查询相同的结果。【参考方案3】:

除非您拥有它但只是不显示它,否则请仔细查看索引,如果没有它们,您在引用除主键之外的任何内容的任何查询上的性能都会下降一个数量级。

另一个主要的性能损失是 LIKE '%nnn%'。

可以用这些做点什么吗?

但是有了一些好的索引,如果你有硬件支持,这个查询应该没问题。

我对具有数百万行的表执行 LIKE 查询。如果查询的其余部分可以消除任何不必要的匹配,这不是问题。

您可以使用子查询来缩小 LIKE 查询的范围。

【讨论】:

是的,我在很多字段上都有索引(包括LIKE 语句中使用的 2 个字段),但我不能 100% 确定它们在正确的字段上。除非用户输入搜索查询,否则 LIKE 语句也会被省略。 查找 EXPLAIN 命令让 sql 引擎自己分析查询——它会告诉你(以一种有点神秘的方式)是否有任何缺失的索引

以上是关于选择预订的快速复杂查询的主要内容,如果未能解决你的问题,请参考以下文章

基于java的家政服务管理系统

按优化排序复杂选择查询

5表中的复杂选择查询

是否有一个复杂的选择查询来解决这个问题

您如何优化这个复杂的 sql 查询,然后选择正确的表索引

子选择使复杂的查询真的很慢