我在SQL中优化某个查询时遇到了一些问题(使用MariaDB),给你一些上下文:我有一个系统,可以在门票上发生“事件”(参见日志条目),但也有除了票证之外的其他一些对象(我为什么要分隔事件和ticket_event表)。我想让所有ticket_events按display_time排序。事件表现在有大约20M行。
CREATE TABLE IF NOT EXISTS `event` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(255) DEFAULT NULL,
`data` text,
`display_time` datetime DEFAULT NULL,
`created_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_for_display_time_and_id` (`id`,`display_time`),
KEY `index_for_display_time` (`display_time`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `ticket_event` (
`id` int(11) NOT NULL,
`ticket_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `ticket_id` (`ticket_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `ticket_event`
ADD CONSTRAINT `ticket_event_ibfk_1` FOREIGN KEY (`id`) REFERENCES `event` (`id`),
ADD CONSTRAINT `ticket_event_ibfk_2` FOREIGN KEY (`ticket_id`) REFERENCES `ticket` (`id`);
如你所见,我已经玩了一些密钥(我也为(id,ticket_id)创建了一个,因为我再次删除它,现在没有显示在这里)我执行的查询:
SELECT * FROM ticket_event
INNER JOIN event ON event.id = ticket_event.id
ORDER BY display_time DESC
LIMIT 25
该查询需要花费很长时间才能执行(如果我对特定的ticket_id进行过滤,则需要大约30秒,如果不对其进行过滤则无法可靠地完成它)。如果我对查询运行一个解释,它会显示它执行一个filesort + temporary:
我用力量指数等玩了一下,但这似乎没有解决任何问题,或者我做错了。
有谁看到我做错了什么或我能在这里优化什么?我非常不希望通过将ticket_id / host_id等添加为列来使“event”成为一个宽表,如果它们不适用则将它们设为NULL。
提前致谢!
答案 0 :(得分:1)
您的查询会选择每行中的每一列,即使您使用LIMIT也是如此。您是否尝试按ID选择一个特定行?
答案 1 :(得分:1)
如果您尝试强制索引,那该怎么办?
SELECT * FROM ticket_event
INNER JOIN event
FORCE INDEX (index_for_display_time)
ON event.id = ticket_event.id
ORDER BY display_time DESC
LIMIT 25;
答案 2 :(得分:0)
KEY `index_for_display_time_and_id` (`id`,`display_time`),
没用;算了吧。它没用,因为你使用的是InnoDB,它将数据“聚集”在PK(id
)上。
请将ticket_event
。id
更改为event_id
。 id
令人困惑,因为它感觉就像映射表的PK一样。可是等等!那没有意义?每个活动只有一张票?那为什么ticket_event
存在呢?为什么不将ticket_id
放入event
?
对于多对多表,请执行
CREATE TABLE IF NOT EXISTS `ticket_event` (
`event_id` int(11) NOT NULL,
`ticket_id` int(11) NOT NULL,
PRIMARY KEY (`event_id`, ticket_id), -- for lookup one direction
KEY (`ticket_id`, event_id) -- for the other direction
) ENGINE=InnoDB DEFAULT;
答案 3 :(得分:0)
尝试这个可能会让你获得更好的表现:
SELECT *
FROM ticket_event
INNER JOIN (select * from event ORDER BY display_time DESC limit 25) as b
ON b.id = ticket_event.id;