mysql - 为什么我的带有多重连接的 MySQL 查询如此慢?

标签 mysql join indexing

我正在尝试对大约 30,000 行的表(history_details)运行查询,并且需要连接多个表(历史记录、房间、室友、属性、列出者、学生和管理员)。

  1. 第一次连接到历史表很简单。
  2. 仅当满足条件时才应添加接下来的 3 个连接(房间、室友、特性)。在本例中,它会查找history_details 表中listing_type 列的值,如果它是适当的值,则应将联接添加到查询中。
  3. 接下来的 3 个连接需要满足几个条件才能包含它们,但在这些情况下,它可以是任何 OR 条件。

                    SELECT SQL_CALC_FOUND_ROWS 
                        history_details.history_id,
                        history_details.listing_type,
                        history_details.listing_id,
                        history_details.date_added,
                        history_details.field,
                        history_details.before_value,
                        history_details.after_value,
                        history_details.edit_group,
                        history_details.email,
                        history_details.name,
                        history_details.correction_message,
                        history_details.photo_filepath,
                        history.history_message,
                        listers.first_name,
                        listers.last_name,
                        students.first_name,
                        students.last_name,
                        admin.first_name,
                        admin.last_name
                    FROM history_details
                    LEFT JOIN history
                        ON history_details.history_id = history.history_id
                    LEFT JOIN rooms
                        ON history_details.listing_type="room" AND rooms.room_id = history_details.listing_id
                    LEFT JOIN roommates
                        ON history_details.listing_type="roommate"
                        AND roommates.roommate_id = history_details.listing_id
                    LEFT JOIN properties
                        ON history_details.listing_type="property"
                        AND properties.property_id = history_details.listing_id
                    LEFT JOIN listers
                        ON (history_details.listing_type="lister" AND listers.lister_id = history_details.listing_id)
                        OR (history_details.listing_type="property" AND listers.lister_id = properties.lister_id)
                        OR (history_details.listing_type="room" AND rooms.lister_type="lister" AND listers.lister_id = rooms.lister_id)
                    LEFT JOIN students
                        ON (history_details.listing_type="student" AND students.student_id = history_details.listing_id)
                        OR (history_details.listing_type="roommate" AND students.student_id = roommates.student_id)
                        OR (history_details.listing_type="room" AND rooms.lister_type="student" AND students.student_id = rooms.lister_id)
                    LEFT JOIN admin
                        ON history_details.listing_type="admin" AND admin.admin_id = history_details.listing_id
                    LIMIT 0,100
    

它给了我正确的结果,但运行需要 73 秒。我已经添加了索引,但我不确定它们是否正在使用。这是我使用 EXPLAIN SELECT SQL_CALC_FOUND_ROWS ....

运行解释时显示的内容

enter image description here

这是我使用 EXPLAIN SELECT .... 运行说明时显示的内容。 enter image description here

这些是我添加到各个表中的索引: 历史详情 enter image description here

属性 enter image description here

房间 enter image description here

室友 enter image description here

你能告诉我我的索引是否正在被使用吗?另外,如何改进查询、表结构或索引来提高速度?

编辑: 感谢 Amadan 的有益建议,现在运行时间为 147 毫秒,而不是之前的 73 秒。这是我最终使用的最终查询:

                SELECT SQL_CALC_FOUND_ROWS 
                    history_details.*,
                    listers.first_name AS lister_first_name,
                    listers.last_name AS lister_last_name,
                    NULL AS student_first_name,
                    NULL AS student_last_name,
                    NULL AS admin_first_name,
                    NULL AS admin_last_name
                FROM history_details
                LEFT JOIN history
                    ON history_details.history_id = history.history_id
                LEFT JOIN listers
                    ON listers.lister_id = history_details.listing_id
                WHERE history_details.listing_type="lister"

                UNION

                SELECT history_details.*,
                    NULL AS lister_first_name,
                    NULL AS lister_last_name,
                    students.first_name AS student_first_name,
                    students.last_name AS student_last_name,
                    NULL AS admin_first_name,
                    NULL AS admin_last_name
                FROM history_details
                LEFT JOIN history
                    ON history_details.history_id = history.history_id
                LEFT JOIN students
                    ON students.student_id = history_details.listing_id
                WHERE history_details.listing_type="student"

                UNION

                SELECT history_details.*,
                    listers.first_name AS lister_first_name,
                    listers.last_name AS lister_last_name,
                    NULL AS student_first_name,
                    NULL AS student_last_name,
                    NULL AS admin_first_name,
                    NULL AS admin_last_name
                FROM history_details
                LEFT JOIN history
                    ON history_details.history_id = history.history_id
                LEFT JOIN properties
                    ON properties.property_id = history_details.listing_id
                LEFT JOIN listers
                    ON listers.lister_id = properties.lister_id
                WHERE history_details.listing_type="property"

                UNION

                SELECT history_details.*,
                    listers.first_name AS lister_first_name,
                    listers.last_name AS lister_last_name,
                    students.first_name AS student_first_name,
                    students.last_name AS student_last_name,
                    NULL AS admin_first_name,
                    NULL AS admin_last_name
                FROM history_details
                LEFT JOIN history
                    ON history_details.history_id = history.history_id
                LEFT JOIN rooms
                    ON rooms.room_id = history_details.listing_id
                LEFT JOIN listers
                    ON (rooms.lister_type="lister" AND listers.lister_id = rooms.lister_id)
                LEFT JOIN students
                    ON (rooms.lister_type="student" AND students.student_id = rooms.lister_id)
                WHERE history_details.listing_type="room"

                UNION

                SELECT history_details.*,
                    NULL AS lister_first_name,
                    NULL AS lister_last_name,
                    students.first_name AS student_first_name,
                    students.last_name AS student_last_name,
                    NULL AS admin_first_name,
                    NULL AS admin_last_name
                FROM history_details
                LEFT JOIN history
                    ON history_details.history_id = history.history_id
                LEFT JOIN roommates
                    ON roommates.roommate_id = history_details.listing_id
                LEFT JOIN students
                    ON students.student_id = roommates.student_id
                WHERE history_details.listing_type="roommate"

                UNION

                SELECT history_details.*,
                    NULL AS lister_first_name,
                    NULL AS lister_last_name,
                    NULL AS student_first_name,
                    NULL AS student_last_name,
                    admin.first_name AS admin_first_name,
                    admin.last_name AS admin_last_name
                FROM history_details
                LEFT JOIN history
                    ON history_details.history_id = history.history_id
                LEFT JOIN admin
                    ON admin.admin_id = history_details.listing_id
                WHERE history_details.listing_type="admin"
                ORDER BY history_details_id DESC
                LIMIT 0,100

最佳答案

keyNULL 的行,它没有对这些表使用任何索引。 IE。 listersstudents 尚未优化。这是因为它的索引是不可能的,因为您使用 OR 来生成它。进行多个查询,每个 history_details.listing_type 一个查询,然后将它们UNION。是的,它看起来很丑,有很多重复,但是速度就在那里。

联合语法如下:

SELECT ...
WHERE history_details.listing_type="lister"
UNION
SELECT ...
WHERE history_details.listing_type="property"
UNION
SELECT ...
WHERE history_details.listing_type="room"
UNION
SELECT ...
WHERE history_details.listing_type="admin"
....
...
ORDER BY whatever
LIMIT whatever, whatever

确保每个SELECT具有相同数量的字段(因此您甚至必须执行无用的连接,但它们会很快,因为它们将被索引)。 ... 基本上是您的整个查询(SELECT field1, field2... FROM table1 LEFT JOIN table2 ON 条件 ...),但使用 OR listerstudent 子句中取出。 UNION 将合并子选择的所有行。 ORDERLIMIT 将应用于并集的结果,而不是最后一个 SELECT

关于mysql - 为什么我的带有多重连接的 MySQL 查询如此慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27562303/

相关文章:

mysql - 索引 3 个整数字段的 mysql 表

mysql - 很多小的 mysql 表或一张大表

excel - 如何根据两列查找值(列值不唯一)

mysql - 查询仍然很慢

mysql - 在 MySQL 和 PostgreSQL 服务器中启用 SSL

java - 在 MySQL 中设计两个表以在 JPA 中使用

MySQL:给出模式的复杂问题

mySQL >> 使用 CONCAT、GROUP_CONCAT 和多个 JOINS 的查询速度非常慢(63 条记录需要 100 秒)

MySQL - 匹配所有标签而不是任何标签

mongodb - NoSQL:索引和基于关键字的搜索