如何解决优化疯狂的 MySQL 查询
我需要优化这个看起来很疯狂的查询(遗留代码):
SELECT
E.eventId,E.currency,COALESCE(ROUND(UR.ratings,2),0) as ratings,COALESCE(UR.ratingCount,0) as ratingCount,E.shopSpaceAvail,E.floorPlanImage,COALESCE(O.goingCount,0) as goingCount,(COALESCE(O.goingGroup,'')) as goingGroup,E.userId,E.name,E.withoutTicket,E.mainImage,E.mainImageThumb,E.privateEvent,E.location,E.locationLatitude,E.locationLongitude,E.country3Code,E.country,E.city,E.description,E.startDt as startDt_formatted,E.endDt as endDt_formatted,(
SELECT
COUNT(*)
FROM
eventIntresteds
WHERE
eventId = E.eventId
) as interestedCount,(
CASE WHEN "kaka" = "" THEN 0 ELSE (
SELECT
COUNT(*)
FROM
eventIntresteds as EI3
WHERE
EI3.eventId = E.eventId
AND EI3.userId IN (
48,1872,2039,67132,1076,1880,3504,3641,4575,3080,67129,67130,67134
)
) END
) as mutualInterestedCount,COALESCE(
(
SELECT
MIN(adultPrice)
FROM
eventTickets as ET
WHERE
ET.deleted = '0'
AND ET.eventId = E.eventId
AND ET.eventTicketType = 'Normal'
),0
) as minPrice,(
CASE WHEN 'kaka' = '' THEN '2' WHEN (
(
SELECT
COUNT(*)
FROM
eventIntresteds EI1
WHERE
EI1.eventId = E.eventId
AND EI1.userId = 2162
) > 0
) THEN '1' ELSE '0' END
) as isInterested,(
ROUND(
(
(
3959 * acos(
cos(radians(0)) * cos(radians(E.locationLatitude)) * cos(radians(E.locationLongitude) - radians(0)) + sin(radians(0)) * sin(radians(E.locationLatitude))
)
) * 1.67
),4
)
) as distance,(
CASE WHEN E.privateEvent = '0'
OR E.userId = 2162 THEN '1' WHEN 'kaka' = '' THEN '0' WHEN (
(
SELECT
COUNT(*)
FROM
userNotifications UN
WHERE
UN.eventId = E.eventId
AND UN.userId = 2162
AND UN.notificationType = 'eventInvite'
) = 0
) THEN '0' ELSE '1' END
) as isprivateEvent,(
CASE WHEN (
E.privateEvent = '0'
or E.userId = 2162
) THEN 1 ELSE (
SELECT
COUNT(*)
FROM
invites AS I
WHERE
I.eventId = E.eventId
AND I.inviteType = 'Event'
AND I.deleted = '0'
AND I.userId = 2162
) END
) as privateHavingCheck,(
SELECT
COUNT(Distinct O1.shopId)
FROM
orders O1
JOIN shops S1 ON (S1.shopId = O1.shopId)
WHERE
O1.eventId = E.eventId
AND S1.deleted = '0'
AND S1.blocked = '0'
AND O1.orderStatus = 'Success'
AND O1.paymentStatus = 'Success'
AND O1.orderType = 'shopBooth'
AND O1.refundId = ''
) as shopCount
FROM
events AS E
JOIN users U ON (
U.userId = E.userId
AND U.blocked = '0'
)
LEFT JOIN (
SELECT
COUNT(*) as ratingCount,AVG(ratings) as ratings,eventId
FROM
userRatings
WHERE
userRatings.blocked = '0'
AND userRatings.deleted = '0'
GROUP BY
eventId
) as UR ON (UR.eventId = E.eventId)
LEFT JOIN (
SELECT
COUNT(*) as goingCount,GROUP_CONCAT(userId) as goingGroup,eventId
FROM
orders AS O
WHERE
O.orderStatus = 'Success'
AND O.paymentStatus = 'Success'
AND orderType = 'EventTicket'
GROUP BY
eventId
) as O ON (O.eventId = E.eventId)
WHERE
E.blocked = '0'
AND E.deleted = '0'
AND E.approved = '1'
AND E.privateEvent = '0'
AND E.endDt >= now()
Having
eventId != 0
AND isprivateEvent = '1'
AND distance <= 1000000
ORDER BY
minPrice DESC,minPrice DESC
LIMIT
0,10;
我尝试为此创建索引,但似乎没有任何帮助。查询超级慢。麦汁部分似乎是这样的:
eventId FROM orders AS O WHERE O.orderStatus='Success' AND O.paymentStatus='Success' AND orderType='EventTicket' GROUP BY eventId) as O ON (O.eventId=E.eventId)
这根据explain()扫描了10K+行。我在 (orderStatus,paymentStatus,orderType) 上尝试了复合索引,但没有帮助。
有关如何快速优化此查询的任何建议?我知道它应该重构,但没有时间。我也知道这很糟糕,所以我不希望它变得非常快。但在这一点上,任何加速都将不胜感激。
这是 MySQL 5.7。
编辑:
解释如下:
+----+--------------------+-----------------+------------+-------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+----------+----------------------------------------+-------+----------+-----------------------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+-----------------+------------+-------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+----------+----------------------------------------+-------+----------+-----------------------------------------------------------------------------------+
| 1 | PRIMARY | E | NULL | ref | userId,combo,events_idx_blocke_delete_approv_privat_enddt | events_idx_blocke_delete_approv_privat_enddt | 4 | const,const,const | 359 | 33.33 | Using index condition; Using where; Using temporary; Using filesort |
| 1 | PRIMARY | U | NULL | eq_ref | PRIMARY,blocked,users_idx_blocked_userid | PRIMARY | 8 | db.E.userId | 1 | 50.00 | Using where |
| 1 | PRIMARY | <derived9> | NULL | ref | <auto_key0> | <auto_key0> | 8 | db.E.eventId | 2 | 100.00 | NULL |
| 1 | PRIMARY | <derived10> | NULL | ref | <auto_key0> | <auto_key0> | 8 | db.E.eventId | 18 | 100.00 | NULL |
| 10 | DERIVED | O | NULL | index_merge | paymentStatus,orderStatus,orderType,eventIdAndDate,eventId,complexIdx3,complexIdx4,complexIdx5,sds | paymentStatus,orderType | 22,22,22 | NULL | 10019 | 100.00 | Using intersect(paymentStatus,orderType); Using where; Using filesort |
| 9 | DERIVED | userRatings | NULL | index | complexIdx1,eventId | eventId | 8 | NULL | 43 | 25.00 | Using where |
| 8 | DEPENDENT SUBQUERY | O1 | NULL | range | shopId,refundId,sds | complexIdx3 | 44 | NULL | 78 | 2.50 | Using index condition; Using where |
| 8 | DEPENDENT SUBQUERY | S1 | NULL | eq_ref | PRIMARY,shopId,deleted,shops_idx_deleted_blocked_shopid | PRIMARY | 8 | db.O1.shopId | 1 | 80.93 | Using where |
| 7 | DEPENDENT SUBQUERY | I | NULL | ref | userId,invites_idx_invitet_deleted_userid_eventid | invites_idx_invitet_deleted_userid_eventid | 39 | const,db.E.eventId | 1 | 100.00 | Using index |
| 6 | DEPENDENT SUBQUERY | UN | NULL | ref | userId,evId,notType,usernotifications_idx_userid_notificati_eventid | usernotifications_idx_userid_notificati_eventid | 38 | const,db.E.eventId | 368 | 100.00 | Using index |
| 5 | DEPENDENT SUBQUERY | EI1 | NULL | eq_ref | eventId_2,userId,eventintresteds_idx_userid_eventid | eventId_2 | 16 |db.E.eventId,const | 1 | 100.00 | Using where; Using index |
| 4 | DEPENDENT SUBQUERY | ET | NULL | ref | eventId,type,delEvId,eventtickets_idx_deleted_eventti_eventid_adultpr | eventtickets_idx_deleted_eventti_eventid_adultpr | 91 | const,db.E.eventId | 24 | 100.00 | Using index |
| 3 | DEPENDENT SUBQUERY | EI3 | NULL | ref | eventId_2,eventintresteds_idx_userid_eventid | eventId_2 | 8 |db.E.eventId | 108 | 0.84 | Using where; Using index |
| 2 | DEPENDENT SUBQUERY | eventIntresteds | NULL | ref | eventId_2,eventId | eventId | 8 |db.E.eventId | 107 | 100.00 | Using where; Using index |
+----+--------------------+-----------------+------------+-------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+----------+----------------------------------------+-------+----------+-----------------------------------------------------------------------------------+
编辑 2:
如果有人可以建议一个或多个索引来加快速度,那将不胜感激。 此查询是由 ORM(Sequilize)生成的,因此没有简单的方法可以手动更改它。
解决方法
(请为每张桌子提供 SHOW CREATE TABLE
。)
总的来说,JOIN ( SELECT ... )
的效率低于其他技术。
Tentatie 索引:
E: INDEX(blocked,deleted,approved,privateEvent,endDt) -- last in this index
orders: INDEX(paymentStatus,orderStatus,orderType,-- these first
refundId,eventId,shopId)
invites: INDEX(userId,inviteType,eventId)
UN: INDEX(notificationType,userId)
eventIntresteds: INDEX(eventId,userId) -- in this order
简化和优化:
( SELECT COUNT(*)
FROM eventIntresteds EI1
WHERE EI1.eventId = E.eventId
AND EI1.userId = 2162 ) > 0 ) THEN '1' ELSE '0' END
-->
EXISTS ( SELECT 1
FROM eventIntresteds EI1
WHERE EI1.eventId = E.eventId
AND EI1.userId = 2162 )
有几个 CASE 子句,一些使用 EXISTS
可能会更好。
请注意,我从您的问题中假设没有机会更改查询语法,因为您说它是由 ORM 生成的,并且您只想以索引的形式进行“快速修复”。
选择列表中的所有子查询在注释中都有 Using index
,因此它们已经在使用覆盖索引。我怀疑您是否可以做任何事情来进一步优化这些。相关的子查询很难优化,因为它们必须对结果的每一行执行一次,before HAVING 子句中的条件。
表 events as E
优化良好,如果我能猜到索引 events_idx_blocke_delete_approv_privat_enddt
位于 WHERE 子句中引用的四列上。
表 users AS U
由 PRIMARY 键访问,因此可能无法使用索引进一步优化它。
派生表UR
需要优化。我会添加这个索引:
ALTER TABLE userRatings ADD INDEX (blocked,ratings);
(我将索引名称留给您,因为您知道要使用什么命名约定。)
派生表 O
正在执行三个单独索引的索引交集。通常单个复合索引比依赖索引合并要好。我会添加这个索引:
ALTER TABLE orders ADD INDEX (orderStatus,paymentStatus,userId);
我知道你说过你在前三列上尝试了复合索引,但添加其他两列可以帮助它作为覆盖索引。
不幸的是,ORDER BY 是按相关子查询的结果排序的,因此无法优化掉 Using filesort
。
这只是一个怪物查询,试图一次做几件事(例如所有相关的子查询)。你做的任何快速修复都不会有太大帮助。您确实需要对其进行重构才能获得重大改进。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。