我正在尝试对大约 30,000 行的表(history_details)运行查询,并且需要连接多个表(历史记录、房间、室友、属性、列出者、学生和管理员)。
- 第一次连接到历史表很简单。
- 仅当满足条件时才应添加接下来的 3 个连接(房间、室友、特性)。在本例中,它会查找history_details 表中listing_type 列的值,如果它是适当的值,则应将联接添加到查询中。
接下来的 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 ....
这是我使用 EXPLAIN SELECT .... 运行说明时显示的内容。
这些是我添加到各个表中的索引: 历史详情
属性
房间
室友
你能告诉我我的索引是否正在被使用吗?另外,如何改进查询、表结构或索引来提高速度?
编辑: 感谢 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
最佳答案
key
为 NULL
的行,它没有对这些表使用任何索引。 IE。 listers
和 students
尚未优化。这是因为它的索引是不可能的,因为您使用 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
从 lister
和 student
子句中取出。 UNION
将合并子选择的所有行。 ORDER
和 LIMIT
将应用于并集的结果,而不是最后一个 SELECT
。
关于mysql - 为什么我的带有多重连接的 MySQL 查询如此慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27562303/