繁体   English   中英

mysql ORDER BY CASE-太慢,更快的方法吗?

[英]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列中使用的列,在基于caseorder子句中使用的列):

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM