以下查询需要超过30秒才能运行,除非:
如何在1秒内运行此查询。必需:如何获得具有相关数据的唯一会议列表,如下面的联接所描述的那样,包括某种类型。
相关数据既用于确定是否存在相关字段,也用于进行GROUP_CONCAT操作 - 因此要求对同一个registereditems表进行3次不同的连接。
提前感谢任何帮助&或建议!我一直在用这个敲打几个小时!
SELECT
DISTINCT( `meetings`.`id` ) AS `meeting_id`,
`meetings`.`uid` AS meeting_uid,
`meetings_SERV`.`id` AS meetings_SERV_id,
`meetings_TRANSP`.`id` AS meetings_TRANSP_id,
`meetings_ACCO`.`id` AS meetings_ACCO_id,
`meetings_BOOKEDITEMS`.`id` AS meetings_BOOKEDITEMS_id
FROM `meetings` AS meetings
LEFT OUTER JOIN `bookeditems` AS `meetings_SERV`
ON `meetings`.`uid` = `meetings_SERV`.`meeting_uid`
AND 'SER' = `meetings_SERV`.`item_type`
LEFT OUTER JOIN `bookeditems` AS `meetings_TRANSP`
ON `meetings`.`uid` = `meetings_TRANSP`.`meeting_uid`
AND 'TRA' = `meetings_TRANSP`.`item_type`
LEFT OUTER JOIN `bookeditems` AS `meetings_ACCO`
ON `meetings`.`uid` = `meetings_ACCO`.`meeting_uid`
AND 'ACC' = `meetings_ACCO`.`item_type`
LEFT OUTER JOIN `bookeditems` AS `meetings_BOOKEDITEMS`
ON `meetings`.`uid` = `meetings_BOOKEDITEMS`.`meeting_uid`
ORDER BY `meetings`.`datetime`
LIMIT 0, 50
CREATE TABLE IF NOT EXISTS `bookeditems` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`meeting_uid` varchar(256) NOT NULL,
`item_type` varchar(256) NOT NULL,
PRIMARY KEY (`id`),
KEY `meeting_uid` (`meeting_uid`(255)),
KEY `index1` (`meeting_uid`(255),`item_type`(255))
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5889 ;
CREATE TABLE IF NOT EXISTS `meetings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` varchar(256) NOT NULL,
`datetime` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`(255)),
KEY `datetime` (`datetime`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=7487 ;
id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
-------------------------------------------------------------------------------------------------------------------------------------
1 | SIMPLE | meetings | ALL | NULL | NULL | NULL | NULL | 7483 | Using temporary; Using filesort
1 | SIMPLE | meetings_SERV | ref | meeting_uid,index1 | meeting_uid | 767 | test.meetings.uid | 1 |
1 | SIMPLE | meetings_TRANSP | ref | meeting_uid,index1 | meeting_uid | 767 | test.meetings.uid | 1 |
1 | SIMPLE | meetings_ACCO | ref | meeting_uid,index1 | meeting_uid | 767 | test.meetings.uid | 1 |
1 | SIMPLE | meetings_BOOKEDITEMS | ref | meeting_uid,index1 | meeting_uid | 767 | test.meetings.uid | 1 |
starting 0.000092
checking permissions 0.000003
checking permissions 0.000002
checking permissions 0.000001
checking permissions 0.000001
checking permissions 0.000003
Opening tables 0.000036
System lock 0.000008
init 0.000033
optimizing 0.000005
statistics 0.000035
preparing 0.000019
Creating tmp table 0.000165
executing 0.000004
Copying to tmp table 1.790968
converting HEAP to MyISAM 1.669041
Copying to tmp table on disk 28.32606
Sorting result 0.141737
Sending data 0.000099
end 0.000005
removing tmp table 0.022097
end 0.000014
query end 0.000008
closing tables 0.000017
freeing items 0.000779
logging slow query 0.000004
cleaning up 0.000005
根据Eric R. Rath的帮助,我已经分析了查询并添加了max_heap_table_size = 256M& tmp_table_size = 256M到MySQL配置我已经能够消除“将HEAP转换为MyISAM”和“复制到磁盘上的tmp表”的执行步骤。
虽然这会使总执行时间缩短到2秒以下,但我仍然不相信这是我能做的一切,如果在查询优化方面有任何其他建议请告诉我。
...
executing 0.000004
Copying to tmp table 1.790968
Sorting result 0.141737
...
答案 0 :(得分:0)
让我们检查索引。
您是否在铺设项目中的meeting_id和item_type上有复合索引?
您是否在会议上有会议ID的索引,如果它在复合键中,它是第一个索引吗?
会议日期时间是否有聚集索引?
您可以将子查询放在每个会议ID的堆栈顶部,而不是区分吗?
类似的东西:
select * from meetings a
where datetime = (select max(datetime) from meetings
where meetingid = a.meetingid)
您可以使用case语句而非会议类型来获取相同的数据,而不是左连接吗?
答案 1 :(得分:0)
我认为index2是不必要的,可以删除;它是index1的前缀。但这不会减少查询时间。
EXPLAIN输出显示真正的罪魁祸首:“使用临时,使用filesort”。有时您可以通过让MySQL使用与加入相同的密钥执行排序来避免这种情况。如果将index1更改为(meeting_id,datetime),则可能会这样做。如果需要在index1中保留item_type,可以将其添加为索引中的第三列,或者在查询中包含所有值的IN()子句。
答案 2 :(得分:0)
您可以首先尝试帮助优化器SELECT
- 感兴趣的记录(使用ORDER BY
和LIMIT
),然后使用该结果加入其他表格。使用此方法,datetime
上的索引可以完全用于ORDER BY
和LIMIT
- 子句。以下是这种方法的样子:
SELECT
`meetings`.`id` AS `meeting_id`,
`meetings`.`uid` AS meeting_uid,
GROUP_CONCAT(
DISTINCT CASE bookeditems.item_type
WHEN 'SER' THEN bookeditems.id
ELSE NULL
END
) AS meetings_SERV_ids,
GROUP_CONCAT(
DISTINCT CASE bookeditems.item_type
WHEN 'TRA' THEN bookeditems.id
ELSE NULL
END
) AS meetings_TRANSP_ids,
GROUP_CONCAT(
DISTINCT CASE bookeditems.item_type
WHEN 'ACC' THEN bookeditems.id
ELSE NULL
END
) AS meetings_ACCO_ids,
GROUP_CONCAT(bookeditems.id) AS meetings_BOOKEDITEMS_ids
FROM (
SELECT id
FROM meetings
ORDER BY `datetime`
LIMIT 0, 50
) filtered_meetings
INNER JOIN meetings
ON meetings.id = filtered_meetings.id
LEFT OUTER JOIN bookeditems
ON meeting_uid = bookeditems.meeting_uid
GROUP BY meeting_uid
显着差异:
GROUP_CONCAT
和CASE
语句来选择会议组中符合特定条件的所有ID。此外,为此,我们需要添加GROUP BY
语句来对表示同一会议的所有行进行分组。meetings_SERV_ids
,meetings_TRANSP_ids
,meetings_ACCO_ids
和meetings_BOOKEDITEMS_ids
获取一系列ID。因此,请记住处理结果行的客户端代码中的explode
或等效内容。