如何优化重复MySQL排序选择

时间:2015-07-01 19:11:12

标签: mysql sorting optimization indexing sql-order-by

假设我有一个包含a,b和c列的大型数据库。假设我希望根据多列上的某些排序选择第x到第(x + 100)行。我可以使用ORDER BYLIMIT构造完成此操作:

SELECT * FROM table_name ORDER BY b ASC, c DESC, a DESC LIMIT x, 100

如果我希望使用相同的顺序进行许多类似的查询(在b上升,在c上降,然后在a上降),但具有不同的范围限制,该怎么办?直观地说,不需要为每个这样的查询重复昂贵的排序操作。

我正在研究使用索引(http://dev.mysql.com/doc/refman/5.6/en/order-by-optimization.html)优化ORDER BY操作,但不幸的是,似乎无法创建包含混合升序和降序的指标。

有优化方法可以优化吗?这似乎是一个相当常见的用例。

2 个答案:

答案 0 :(得分:1)

这是一个可能适用于数字列的想法(这是一个黑客攻击)。 对于要按其排序的每个列,添加一个具有值MAX_TYPE - column_value的相同类型的新列,其中MAX_TYPE是此列所需的最大值。现在为此列添加一个索引,然后按它而不是原始列进行排序。

抬头:

  • 我使用DECIMAL代替DOUBLE,因为双倍可能会出现舍入错误。
  • 也许我错过了一些东西,因为MySQL在使用ORDER BY时根本不使用任何索引(即使只有一列)。
  • @Rick James建议的解决方案肯定比使用MAX_TYPE更好。

SQL fiddle:

MySQL 5.6架构设置

CREATE TABLE `bogus` (
  `income` DECIMAL(7,2),
  `expense` DECIMAL(7,2),
  `expense_inverted` DECIMAL(7,2)
);

ALTER TABLE `bogus` ADD INDEX `income_idx` (`income`);
ALTER TABLE `bogus` ADD INDEX `expense_idx` (`expense`);
ALTER TABLE `bogus` ADD INDEX `expense_inverted_idx` (`expense_inverted`);

INSERT INTO `bogus` (`income`, `expense`)
  VALUES
  (250.35, 200.90),
  (250.35, 100.35),
  (300.50, 210.75);

UPDATE `bogus` SET `expense_inverted` = 99999.99 - `expense`;

查询1

SELECT income, expense
FROM `bogus`
ORDER BY
  `income` ASC,
  `expense_inverted` ASC; # equivalent of `expense` DESC

<强> Results

| income | expense |
|--------|---------|
| 250.35 |   200.9 |
| 250.35 |  100.35 |
|  300.5 |  210.75 |

我知道这是一个非常不优雅的解决方案,但对于无法牺牲速度的大型数据库 - 这可能会有效。

答案 1 :(得分:1)

可能唯一的优化是将数字b存储为-b,或者在其中有一个冗余的-b列。然后

ORDER BY b ASC, c DESC LIMIT...

将被

取代
ORDER BY minusb DESC, c DESC LIMIT...

并且

INDEX(minusb, c)

只要你确定

  • 所有ORDER BY项都是同一个表中的列名
  • 方向相同,
  • 并且存在INDEX,其列出的所有顺序与ORDER BY列表的顺序相同(可选 end 上的额外列),

然后优化器可以(但可以选择不)非常有效地使用INDEX - 包括使用LIMIT

无论是ASC还是全部DESC,都无关紧要。 (ASC 可能稍好一些。)

请注意,LIMIT m, n必须阅读m+n行。 (OFFSET是一个很好的功能,但它没有得到很好的优化。)如果您使用OFFSETLIMIT通过长列表“分页”,最好“记住你在哪里停止“以避免扫描OFFSET行。 (如果适用,我可以提供更多详细信息。)