[英]mysql ORDER BY with CASE - too slow, faster way?
像这样用CASE看一下ORDER BY的各种答案时 ,我发现我不得不在这个遗留应用程序中做的事情很可能是一种专家方法。 但是,当行数少于平凡时(行数超过100,000会导致10秒钟的页面加载),速度太慢。
请注意,原始查询试图解决一个看似常见的问题,其中查询分析人员需要的日期与常规的排序方式相比是空的。 在这种情况下, datefirstprinted
将降序,但是所有未打印的记录应填充到列表的顶部。
原来的查询解决了这个,但问题的关键是要避免filesort
自带的派生列的性能损失notprintedyet
。
原始查询
SELECT SQL_NO_CACHE
id, daterun, datefirstprinted,
case datefirstprinted when "0000-00-00 00:00:00" then 1 else 0 end as notprintedyet
FROM
patientrecords
WHERE
dateuploaded <> '0000-00-00 00:00:00'
ORDER BY
notprintedyet desc, /* ordered via alias */
datefirstprinted desc
LIMIT 10;
时间1.52s
我发现不对别名notprintedyet
排序可以节省一些时间:
查询速度稍快
SELECT SQL_NO_CACHE
id, daterun, datefirstprinted,
case datefirstprinted when "0000-00-00 00:00:00" then 1 else 0 end as notprintedyet
FROM
patientrecords
WHERE
dateuploaded <> '0000-00-00 00:00:00'
ORDER BY
datefirstprinted = "0000-00-00 00:00:00" desc, /* directly ordered */
datefirstprinted
LIMIT 10;
时间1.37s
最佳速度,但首先缺少必需的空日期排序
SELECT SQL_NO_CACHE
id, daterun, datefirstprinted,
case datefirstprinted when "0000-00-00 00:00:00" then 1 else 0 end as notprintedyet
FROM
patientrecords
WHERE
dateuploaded <> '0000-00-00 00:00:00'
ORDER BY
datefirstprinted /* not ordered properly */
LIMIT 10;
时间0.48s
我尝试使用视图
create view notprinted_patientrecords as (
SELECT id, daterun, datefirstprinted, case datefirstprinted when "0000-00-00 00:00:00" then 1 else 0 end notprintedyet
FROM patientrecords
WHERE dateuploaded <> '0000-00-00 00:00:00'
);
不幸的是,当我跑解释
explain select * from notprinted_patientrecords order by notprintedyet desc limit 10;
它表明我仍在使用filesort
并花费 1.51s, 也没有节省
如果datefirstprinted默认为NULL会更快吗?
也许可以,但是在这个旧版应用中,危害可能比页面加载时间多出5秒更大
我们还能尝试什么? 存储过程? 职能?
更新
如建议的@strawberry-按案例订购
...
ORDER BY
case datefirstprinted when "0000-00-00 00:00:00" then 1 else 0 end, datefirstprinted
LIMIT 10;
时间1.52s
按照@ e4c5的要求, explain
输出:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: patientrecords
type: range
possible_keys: dateuploaded,uploads_report
key: dateuploaded
key_len: 5
ref: NULL
rows: 299095
Extra: Using index condition; Using filesort
除非订购不正确 ,但有以下差异
rows: 10
Extra: Using where
创建表语句
*************************** 1. row ***************************
Table: patientrecords
Create Table: CREATE TABLE `patientrecords` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`datecreated` datetime NOT NULL,
`dateuploaded` datetime NOT NULL,
`daterun` datetime NOT NULL,
`datebilled` datetime NOT NULL,
`datefirstprinted` datetime NOT NULL,
`datelastprinted` datetime NOT NULL,
`client` varchar(5) NOT NULL,
PRIMARY KEY (`id`),
KEY `dateuploaded` (`dateuploaded`),
KEY `daterun` (`daterun`),
KEY `uploads_report` (`dateuploaded`,`client`),
KEY `datefirstprinted` (`datefirstprinted`),
KEY `datelastprinted` (`datelastprinted`)
)
查看您的表,首先要注意的是,以下索引是多余的
KEY `dateuploaded` (`dateuploaded`),
这个角色可以充分发挥它的作用
KEY `uploads_report` (`dateuploaded`,`client`),
因此,让我们删除dateuploaded
键。 目前尚不清楚您是否在任何查询中实际使用了client列。 如果您不这样做,我相信按照以下方式更改索引将大大提高您的速度
KEY `uploads_report` (`dateuploaded`,`datefirstprinted`,`client`),
这是因为mysql每个表只能使用一个索引。 由于在where子句中使用了dateuploaded列的索引,因此无法使用datefirstprinted
的索引。 但是,如果将两列合并到同一索引中,则可以在sort和where中使用它。
完成上述索引后,可能会删除该索引:
KEY `datefirstprinted` (`datefirstprinted`),
较少的索引将使您的插入和更新速度更快。
在@ e4c5的帮助下,遵循在级联索引上学到的想法,我尝试在两列上添加键(在where
列中使用的列,在基于case
的order
子句中使用的列):
alter table
patientrecords
add index
printedvsuploaded (datefirstprinted, dateuploaded);
由于mysql继续使用index dateuploaded
因此最初没有效果。
但是,添加force index
会减少查询时间:
SELECT SQL_NO_CACHE
id, daterun, datefirstprinted
FROM
patientrecords
FORCE INDEX (printedvsuploaded)
WHERE
dateuploaded <> '0000-00-00 00:00:00'
ORDER BY
case when datefirstprinted = "0000-00-00 00:00:00" then 1 else 0 end desc,
datefirstprinted
LIMIT 10;
时间0.64秒
值得注意的是,我同意@ e4c5的观点,即额外的索引最终将导致写入性能下降; 我指望其他路线图的发展,以帮助减少索引数。 现在,实施此操作会将较大结果集的10秒页面加载减少到3秒的可管理范围内,然后将要实施的解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.