您的原始查询
SELECT * FROM Board ORDER by root DESC, path ASC LIMIT 0,100;
创建一个名为 BoardDisplayOrder 的表来保存 root 的负值,在其中添加名为 rootinv 的新列。
首先是示例数据和您的原始查询:
mysql> drop database if exists YourCommonSense;
Query OK, 2 rows affected (0.06 sec)
mysql> create database YourCommonSense;
Query OK, 1 row affected (0.00 sec)
mysql> use YourCommonSense
Database changed
mysql> CREATE TABLE `Board` (
-> `id` int(11) NOT NULL AUTO_INCREMENT,
-> `path` varchar(255) NOT NULL DEFAULT '0',
-> `root` int(11) NOT NULL DEFAULT '0',
-> PRIMARY KEY (`id`),
-> KEY `root` (`root`),
-> KEY `path` (`path`),
-> KEY `rootpath` (`root`,`path`)
-> );
Query OK, 0 rows affected (0.11 sec)
mysql> INSERT INTO Board (path,root) VALUES
-> ('Rolando Edwards',30),
-> ('Daniel Edwards',30),
-> ('Pamela Edwards',30),
-> ('Dominiuqe Edwards',40),
-> ('Diamond Edwards',40),
-> ('Richard Washington',50),
-> ('George Washington',50),
-> ('Synora Washington',50);
Query OK, 8 rows affected (0.05 sec)
Records: 8 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM Board;
+----+--------------------+------+
| id | path | root |
+----+--------------------+------+
| 2 | Daniel Edwards | 30 |
| 3 | Pamela Edwards | 30 |
| 1 | Rolando Edwards | 30 |
| 5 | Diamond Edwards | 40 |
| 4 | Dominiuqe Edwards | 40 |
| 7 | George Washington | 50 |
| 6 | Richard Washington | 50 |
| 8 | Synora Washington | 50 |
+----+--------------------+------+
8 rows in set (0.00 sec)
mysql> SELECT * FROM Board ORDER by root DESC, path ASC LIMIT 0,100;
+----+--------------------+------+
| id | path | root |
+----+--------------------+------+
| 7 | George Washington | 50 |
| 6 | Richard Washington | 50 |
| 8 | Synora Washington | 50 |
| 5 | Diamond Edwards | 40 |
| 4 | Dominiuqe Edwards | 40 |
| 2 | Daniel Edwards | 30 |
| 3 | Pamela Edwards | 30 |
| 1 | Rolando Edwards | 30 |
+----+--------------------+------+
8 rows in set (0.00 sec)
mysql> EXPLAIN SELECT * FROM Board ORDER by root DESC, path ASC LIMIT 0,100;
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+
| 1 | SIMPLE | Board | index | NULL | rootpath | 261 | NULL | 8 | Using index; Using filesort |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+
1 row in set (0.00 sec)
mysql>
接下来,使用 rootinv 和涉及 rootinv 的索引创建表 BoardDisplayOrder:
mysql> CREATE TABLE BoardDisplayOrder LIKE Board;
Query OK, 0 rows affected (0.09 sec)
mysql> ALTER TABLE BoardDisplayOrder DROP INDEX root;
Query OK, 0 rows affected (0.11 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE BoardDisplayOrder DROP INDEX path;
Query OK, 0 rows affected (0.09 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE BoardDisplayOrder DROP INDEX rootpath;
Query OK, 0 rows affected (0.08 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE BoardDisplayOrder ADD COLUMN rootinv int(11) NOT NULL;
Query OK, 0 rows affected (0.17 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE BoardDisplayOrder ADD INDEX rootpathid (rootinv,path,id,root);
Query OK, 0 rows affected (0.11 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SHOW CREATE TABLE BoardDisplayOrder \G
*************************** 1. row ***************************
Table: BoardDisplayOrder
Create Table: CREATE TABLE `boarddisplayorder` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`path` varchar(255) NOT NULL DEFAULT '0',
`root` int(11) NOT NULL DEFAULT '0',
`rootinv` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `rootpathid` (`rootinv`,`path`,`id`,`root`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql>
然后,填充 BoardDisplayOrder:
mysql> INSERT INTO BoardDisplayOrder (id,path,root,rootinv)
-> SELECT id,path,root,-root FROM Board;
Query OK, 8 rows affected (0.06 sec)
Records: 8 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM BoardDisplayOrder;
+----+--------------------+------+---------+
| id | path | root | rootinv |
+----+--------------------+------+---------+
| 7 | George Washington | 50 | -50 |
| 6 | Richard Washington | 50 | -50 |
| 8 | Synora Washington | 50 | -50 |
| 5 | Diamond Edwards | 40 | -40 |
| 4 | Dominiuqe Edwards | 40 | -40 |
| 2 | Daniel Edwards | 30 | -30 |
| 3 | Pamela Edwards | 30 | -30 |
| 1 | Rolando Edwards | 30 | -30 |
+----+--------------------+------+---------+
8 rows in set (0.00 sec)
mysql>
现在,针对 BoardDisplayOrder 运行查询,但在 rootinv 上没有 DESC:
mysql> SELECT id,path,root FROM BoardDisplayOrder ORDER by rootinv, path LIMIT 0,100;
+----+--------------------+------+
| id | path | root |
+----+--------------------+------+
| 7 | George Washington | 50 |
| 6 | Richard Washington | 50 |
| 8 | Synora Washington | 50 |
| 5 | Diamond Edwards | 40 |
| 4 | Dominiuqe Edwards | 40 |
| 2 | Daniel Edwards | 30 |
| 3 | Pamela Edwards | 30 |
| 1 | Rolando Edwards | 30 |
+----+--------------------+------+
8 rows in set (0.00 sec)
mysql> EXPLAIN SELECT id,path,root FROM BoardDisplayOrder ORDER by rootinv, path LIMIT 0,100;
+----+-------------+-------------------+-------+---------------+------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------------+-------+---------------+------------+---------+------+------+-------------+
| 1 | SIMPLE | BoardDisplayOrder | index | NULL | rootpathid | 269 | NULL | 8 | Using index |
+----+-------------+-------------------+-------+---------------+------------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql>
试试看!!!
警告
这很容易做到,因为 root 是 INT。
如果 root 是 VARCHAR,则 rootinv 必须是字符触发器。换句话说,
A
->Z
B
->Y
- ...
M
->N
N
->M
- ...
Y
->B
Z
->A
这原则上适用于您需要执行 DESC 的任何字段。问题源于 MySQL 没有在索引中将键内部排序为 ASC 或 DESC。索引中的所有内容都在升序。这就是为什么当您在 中看到处理程序统计信息时SHOW GLOBAL STATUS LIKE 'handler%';
,您会看到以下内容:
等等。
根据当前的 MySQL 文档
index_col_name 规范可以以 ASC 或 DESC 结尾。这些关键字被允许用于指定升序或降序索引值存储的未来扩展。目前,它们被解析但被忽略;索引值始终按升序存储。
试试看!!!
更新 2012-05-04 06:54 EDT
@frail 对我的回答的评论
ALTER TABLE BoardDisplayOrder ADD INDEX rootpathid (rootinv,path,id,root) 对我来说似乎没有必要,ALTER TABLE BoardDisplayOrder ADD INDEX rootpathid (rootinv,path) 就足够了
我的解决方案的原因ALTER TABLE BoardDisplayOrder ADD INDEX rootpathid (rootinv,path,id,root)
是提供一个覆盖索引。在这种情况下,覆盖索引将:
- 总是有检索所需的列
- 将提高解释计划的质量,因为
- 查询永远不会从表中读取数据以进行数据检索
- 查询将只从索引中读取数据检索
- 导致索引范围扫描
想想原来的查询,
SELECT * FROM Board ORDER by root DESC, path ASC LIMIT 0,100;
这需要检索三列 path、id 和 root。因此,它们需要在索引中。当然,指数规模的增加将是一种权衡。如果 Board 表很大,如果可以更快地检索,有些人不会担心空间。如果根路径索引只是 (rootinv,path),那么每次索引范围扫描都将伴随着对剩余列的表的 ref 查找。这就是我选择的原因ALTER TABLE BoardDisplayOrder ADD INDEX rootpathid (rootinv,path,id,root);