使用 ORDER BY 和多个连接优化 MySQL 查询

标签 mysql sql

我有以下表格(所有 InnoDb):

 CREATE TABLE `user` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`num` INT(11) NOT NULL,
`date` DATETIME NOT NULL DEFAULT '2014-01-01 00:00:00',
`year` VARCHAR(4) NOT NULL DEFAULT '1999',
PRIMARY KEY (`id`),
INDEX `yearIDX` (`year`, `num`),
INDEX `date` (`year`, `date`),
INDEX `dateonly` (`date`),
)


CREATE TABLE `log` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`unit_id` BIGINT(20) NULL DEFAULT NULL
PRIMARY KEY (`id`),
INDEX `logunitIDX` (`unit_id`),
CONSTRAINT `logunitIDX` FOREIGN KEY (`unit_id`) REFERENCES `unit` (`id`)
)

CREATE TABLE `user_log` (
`user_id` BIGINT(20) NOT NULL,
`log_id` BIGINT(20) NOT NULL,
`user_log_idx` INT(11) NOT NULL,
PRIMARY KEY (`user_id`, `user_log_idx`),
INDEX `user_log_key` (`user_id`),
INDEX `user_log` (`log_id`),
INDEX `Index 4` (`user_id`, `log_id`, `user_log_idx`),
CONSTRAINT `user_log` FOREIGN KEY (`log_id`) REFERENCES `log` (`id`),
CONSTRAINT `user_log_key` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
)

CREATE TABLE `unit` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`code` SMALLINT(6) NOT NULL
PRIMARY KEY (`id`),
UNIQUE INDEX `codeIDX_U` (`code`)
)

和下面的查询

SELECT 
    user0_.id AS e1_26_, 
    user0_.`num` AS registry4_26_, 
    user0_.`date` AS date5_26_
FROM `user` user0_
INNER JOIN `user_log` userlog1 
ON  user0_.id=userlog1.`user_id`
INNER JOIN `log` userlo2 
ON userlog1.`log_id`=userlo2.id
INNER JOIN `unit` unit3_ 
ON userlo2.`unit_id`=unit3_.id
WHERE 
user0_.`year`='2014' 
AND 
(unit3_.`code` in ('41','42','43','45'))
ORDER BY user0_.`date` DESC
LIMIT 400

查询分析器显示了这一点(为简洁起见,我省略了 select_type 和 key_len。select_type 很简单)

id table      type   possible_keys key       ref        rows Extra
1  unit3_     range  PRIMARY,
                     codeIDX_U     codeIDX_U  Null       10    Using where; 
                                                               Using index; 
                                                               Using temporary;
                                                               Using filesort
1  userlo2    ref    PRIMARY,
                     logunitIDX,
                     Index 6       logunitIDX unit3_.id  1194   Using where; 
                                                               Using index
1  userlog1   ref    PRIMARY,
                     user_log_key,
                     user_log,
                     Index 4        user_log   userlo2.id 1     Using index

1  user0_   eq_ref  PRIMARY,
                    yearIDX,
                    date            PRIMARY    userlog1.user_id 1   Using where

我相信我在第一行得到“使用临时文件;使用文件排序”的原因是因为“我正在加入许多 ORDER BY 中的表和列“不是来自第一个非常量表来检索行”

是否有人知道是否可以优化此查询以不使用临时/文件排序?

最佳答案

您选择的所有内容都来自 user0_,因此您似乎在使用 join 进行过滤。假设您不关心多个 行,您可以使用exists 重写查询。这可能允许优化器摆脱文件排序:

SELECT u0.id AS e1_26_, u0.`num` AS registry4_26_, u0.`date` AS date5_26_
FROM `user` u0
WHERE EXISTS (SELECT 1
              FROM `user_log` userlog1 INNER JOIN
                   `log` userlo2 
                   ON userlog1.`log_id`= userlo2.id INNER JOIN
                   `unit` unit3_ 
                   ON userlo2.`unit_id`= unit3_.id
                   WHERE u0.id = userlog1.`user_id` AND
                         unit3_.`code` in ('41', '42', '43', '45')
             ) AND 
      u0.`year` = '2014' 
ORDER BY u0.`date` DESC
LIMIT 400;

关于使用 ORDER BY 和多个连接优化 MySQL 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30070929/

相关文章:

MySQL存储过程和游标

mysql - 如何对数据库中的多个表运行相同的查询

mysql - 在 latin1 字符集 MySQL 表上区分大小写 INSERT IGNORE

mysql - 编辑 :SQL multiple table instances

sql - 如何从sqlite查询中获取顶部组?

mysql - 如何用MySql中同一个表中2条不同记录的总和来更新表中的所有列?

mysql - 从 select mysql 中的 url 中提取 id

MySQL 查询用于使用 HTML 表单复选框检索记录

c# - 包含 2 个数据库的 SQL 查询

c# - 一对多的关系总是让我感到空虚