Mysql 使用ORDER BY on DATETIME列优化查询
下面的查询依赖于Mysql 使用ORDER BY on DATETIME列优化查询,mysql,sql,Mysql,Sql,下面的查询依赖于链接表中约4k行和注释表中约40k行的表,目前大约需要0.2秒的时间,考虑到没有那么多数据,这似乎相当慢 SELECT t1.id, t1.url, t1.dateAdded FROM links AS t1 LEFT JOIN comments AS t2 ON (t1.id = t2.linkId) WHERE COALESCE(t2.dateAdded, t1.dateAdded) <= "2020-03-22 20:04:45" GROUP BY t
链接表中约4k行和注释表中约40k行的表,目前大约需要0.2秒的时间,考虑到没有那么多数据,这似乎相当慢
SELECT
t1.id, t1.url, t1.dateAdded
FROM links AS t1 LEFT JOIN
comments AS t2
ON (t1.id = t2.linkId)
WHERE
COALESCE(t2.dateAdded, t1.dateAdded) <= "2020-03-22 20:04:45"
GROUP BY t1.id
ORDER BY
COALESCE(
(
SELECT
MAX(dateAdded)
FROM comments
WHERE
linkId = t1.id AND
dateAdded <= "2020-03-22 20:04:45"
),
t1.dateAdded
) DESC,
t1.id DESC
LIMIT 10
因此,我试图通过使用EXPLAIN
找到差异,似乎唯一的差异在于Extra
字段,其中对于按t1排序。id
为空,对于按t1排序。dateAdded
使用的是临时;使用filesort
(注意,我在t1.dateAdded上有索引)。不幸的是,我在解释这意味着什么以及如何优化原始查询方面有点困难。请注意,id
是INT(10)
,dateAdded
是DATETIME
一般来说,我想达到的目的是订购链接,以便最新的链接或链接与最新的评论是在顶部,“最新”的意思是相对于所提供的时间(即不考虑链接/评论之后添加)。
提前感谢您的帮助或提示
编辑:添加更多详细信息
EXPLAIN
用于使用t1.id的简化查询
+------+-------------+-------+-------+---------------+------------+---------+--------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+-------+---------------+------------+---------+--------------+------+-------------+
| 1 | SIMPLE | t1 | index | NULL | PRIMARY | 4 | NULL | 3674 | |
| 1 | SIMPLE | t2 | ref | fk_link_id | fk_link_id | 5 | db1.t1.id | 8 | Using where |
+------+-------------+-------+-------+---------------+------------+---------+--------------+------+-------------+
EXPLAIN
用于使用t1.dateAdded进行简化查询
+------+-------------+-------+-------+---------------+------------+---------+--------------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+-------+---------------+------------+---------+--------------+------+---------------------------------+
| 1 | SIMPLE | t1 | index | NULL | PRIMARY | 4 | NULL | 3674 | Using temporary; Using filesort |
| 1 | SIMPLE | t2 | ref | fk_link_id | fk_link_id | 5 | db1.t1.id | 8 | Using where |
+------+-------------+-------+-------+---------------+------------+---------+--------------+------+---------------------------------+
有关链接的信息
表格:
CREATE TABLE `links` (
`id` int(10) UNSIGNED NOT NULL,
`url` varchar(2083) CHARACTER SET utf8mb4 NOT NULL,
`dateAdded` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `links`
ADD PRIMARY KEY (`id`),
ADD KEY `dateAdded` (`dateAdded`);
CREATE TABLE `comments` (
`id` int(10) UNSIGNED NOT NULL,
`linkId` int(10) UNSIGNED DEFAULT NULL,
`userId` int(10) UNSIGNED NOT NULL,
`content` varchar(2000) CHARACTER SET utf8mb4 NOT NULL,
`dateAdded` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `comments`
ADD PRIMARY KEY (`id`),
ADD KEY `fk_link_id` (`linkId`);
ALTER TABLE `comments`
ADD CONSTRAINT `fk_link_id` FOREIGN KEY (`linkId`) REFERENCES `links` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
有关注释的信息
表格:
CREATE TABLE `links` (
`id` int(10) UNSIGNED NOT NULL,
`url` varchar(2083) CHARACTER SET utf8mb4 NOT NULL,
`dateAdded` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `links`
ADD PRIMARY KEY (`id`),
ADD KEY `dateAdded` (`dateAdded`);
CREATE TABLE `comments` (
`id` int(10) UNSIGNED NOT NULL,
`linkId` int(10) UNSIGNED DEFAULT NULL,
`userId` int(10) UNSIGNED NOT NULL,
`content` varchar(2000) CHARACTER SET utf8mb4 NOT NULL,
`dateAdded` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `comments`
ADD PRIMARY KEY (`id`),
ADD KEY `fk_link_id` (`linkId`);
ALTER TABLE `comments`
ADD CONSTRAINT `fk_link_id` FOREIGN KEY (`linkId`) REFERENCES `links` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
首先,我可以指出,查询中的分组依据
是不必要的(尽管没有错),因为您没有选择任何聚合。除此之外,我觉得只要使用MAX()
作为分析函数,然后以此排序,您的生活就会变得更轻松。考虑这个版本:
WITH cte AS (
SELECT t1.id, t1.url, t1.dateAdded,
MAX(t2.dateAdded) OVER (PARTITION BY t1.id) maxDateAdded
FROM links AS t1
LEFT JOIN comments AS t2 ON t1.id = t2.linkId
WHERE
(t2.dateAdded IS NOT NULL AND t2.dateAdded <= '2020-03-22 20:04:45') OR
(t2.dateAdded IS NULL AND t1.dateAdded <= '2020-03-22 20:04:45')
)
SELECT id, url, dateAdded
FROM cte
ORDER BY maxDateAdded DESC, t1.id DESC
LIMIT 10;
如果使用这些索引,将加快连接速度,并允许对MAX
的调用快速求值。请注意,我已将WHERE
子句重写为可排序,避免调用COALESCE
请为这两个查询显示解释输出在执行之前,请向我们显示示例数据和预期输出。用简单的英语来说,ORDER BY
子句的逻辑是什么?您应该使用禁用查询缓冲的tbh测试您的查询。该问题包括相关的CREATE TABLE
和CREATE INDEX
语句。另外@nbk和@TimBiegeleisen.Side注意:字符串和日期文字最好用单引号引起来。MySQL接受双引号,但几乎所有其他DBM都不接受。因此,使用单引号将使代码更易于移植。非常感谢您的回答!我可能把事情搞糊涂了,我有:innodb_版本5.6.36-82.1
和10.1.26-MariaDB-0+deb9u1
@leopik:MariaDB 10.2中引入了分析窗口函数:。您可能需要升级。