Mysql 如何优化这个简单的连接+;按查询订购?
我有两个mysql表:Mysql 如何优化这个简单的连接+;按查询订购?,mysql,sql,query-optimization,Mysql,Sql,Query Optimization,我有两个mysql表: /* Table users */ CREATE TABLE IF NOT EXISTS `users` ( `Id` int(10) unsigned NOT NULL AUTO_INCREMENT, `DateRegistered` datetime NOT NULL, PRIMARY KEY (`Id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /* Table statistics_user */ CREATE T
/* Table users */
CREATE TABLE IF NOT EXISTS `users` (
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`DateRegistered` datetime NOT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/* Table statistics_user */
CREATE TABLE IF NOT EXISTS `statistics_user` (
`UserId` int(10) unsigned NOT NULL AUTO_INCREMENT,
`Sent_Views` int(10) unsigned NOT NULL DEFAULT '0',
`Sent_Winks` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`UserId`),
CONSTRAINT `statistics_user_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `users` (`Id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
两个表都填充了10.000个随机行,以便使用以下程序进行测试:
DELIMITER //
CREATE DEFINER=`root`@`localhost` PROCEDURE `FillUsersStatistics`(IN `cnt` INT)
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE dt DATE;
DECLARE Winks INT DEFAULT 1;
DECLARE Views INT DEFAULT 1;
WHILE (i<=cnt) DO
SET dt = str_to_date(concat(floor(1 + rand() * (9-1)),'-',floor(1 + rand() * (28 -1)),'-','2011'),'%m-%d-%Y');
INSERT INTO users (Id, DateRegistered) VALUES(i, dt);
SET Winks = floor(1 + rand() * (30-1));
SET Views = floor(1 + rand() * (30-1));
INSERT INTO statistics_user (UserId, Sent_Winks, Sent_Views) VALUES (i, Winks, Views);
SET i=i+1;
END WHILE;
END//
DELIMITER ;
CALL `FillUsersStatistics`(10000);
。。我明白这一点:
Id select_type table type possible_keys key key_len ref rows extra
1 SIMPLE t1 ALL PRIMARY (NULL) (NULL) (NULL) 10037 Using temporary; Using filesort
1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test2.t2.UserId 1
当两个表都有超过500K行时,上面的查询会变得非常慢。我猜这是因为“使用临时的;在查询解释中使用filesort
如何优化上述查询以使其运行更快?您应该尝试内部联接,而不是笛卡尔积,接下来您可以做的是根据注册日期进行分区。我无法确定ORDER BY是最要命的,因为它无法正确索引。这里有一个可行的解决方案,如果不是特别漂亮的话 首先,假设您有一个名为
Score
的列,用于存储用户的当前分数。每当用户的Sent_视图
或Sent_视图
发生更改时,请修改得分
列以匹配。这可能是通过触发器实现的(我对触发器的经验有限),或者肯定是通过更新Sent\u视图
和Sent\u Winks
字段的相同代码实现的。此更改不需要知道DATEDIFF部分,因为它只需除以旧的Sent\u Views+Sent\u Winks
之和,然后乘以新的部分即可
现在,您只需每天更改一次Score
列(如果您对用户注册的准确小时数不挑剔的话)。这可以通过cron作业运行的脚本来完成
然后,只需索引得分
列并选择离开
注意:编辑以删除不正确的第一次尝试。我提供我的评论作为答案: 确定一个未来的日期,远到不影响你的申请,比如说5000年。在分数计算中,将当前日期替换为未来日期。分数计算现在对于所有意图和目的都是绝对的,并且可以在更新眨眼和视图时计算(通过存储过程或atrigger(mysql是否有触发器?) 将
score
列添加到statistics\u user
表中,以存储计算的分数并在其上定义索引
您的SQL可以重写为:
SELECT
UserId, score
FROM
statistics_user
ORDER BY score DESC
如果您需要实际分数,只需一个常量乘法就可以轻松计算出来,如果它与mysql索引选择相冲突,之后就可以进行计算。您不应该在用户中注册索引日期吗 您正在根据无法索引的动态属性(now())对整个结果进行排序。如果你能在统计数据更新时计算分数,并在分数上维护一个索引,你会有更好的机会。只是一个想法:如果你不是现在(),而是使用一个很长的未来时间(就像你要计算这个结果,比如说在2500年),绝对分数会有所不同,但相对顺序会保持不变。因此,您可以维护一个分数索引,该索引将反映您想要的排序,并且可能会重新计算排序结果的实际分数。真正的问题是:为什么要维护两个表(实际上)之间具有1::1关系?但是使用to_days()不能给出正确的排序顺序。如果我们都有1000次眨眼和观看,但我昨天注册了,你100天前注册了,你会得到更高的分数,但应该是相反的。谢谢你指出……显然我的大脑没有正确连接。我尝试了Chris的建议,但没有解决我的问题。还有其他建议吗?Chris,谢谢你的编辑,但是你能详细说明一下吗:“这个更改不需要知道DATEDIFF部分,因为它可以被旧的Sent_Views+Sent_Winks之和除以,再乘以新的。”。也许我遗漏了什么,但是在不知道DATEDIFF(现在(),DateRegistered)的情况下,如何更新分数呢?也许您的意思是,稍后将使用cron作业重新计算分数,该作业将考虑DATEDIFF(现在(),DateRegistered)?@user1009456我的意思是,每次用户的视图或眼色更改时,您只需将现有分数乘以(new_sum/old_sum)。分数的DATEDIFF部分仅每24小时计算一次。如果你需要更多的澄清,请告诉我。
SELECT
UserId, score
FROM
statistics_user
ORDER BY score DESC