Ответ 1
Ваш исходный запрос
SELECT * FROM Board ORDER by root DESC, path ASC LIMIT 0,100;
Создайте таблицу для хранения отрицательного значения root, называемого BoardDisplayOrder, где вы добавляете новый столбец с именем 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>
Затем создайте таблицу BoardDisplayOrder с помощью rootinv и индекса с участием rootinv:
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, но без DESC на rootinv:
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>
Дайте ему попробовать!!!
CAVEAT
Это было легко сделать, потому что 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;
Для этого требуется получить три столбца пути, id и root. Таким образом, они должны быть в индексе. Конечно, увеличение размера индекса было бы компромиссом. Если таблица Board была очень большой, некоторые не беспокоились бы о пространстве, если бы поиск мог быть сделан быстрее. Если индекс корневых путей был справедливым (rootinv, path), то каждое сканирование диапазона индекса сопровождалось бы поиском ref в таблице для остальных столбцов. Именно по этой причине я выбрал ALTER TABLE BoardDisplayOrder ADD INDEX rootpathid (rootinv,path,id,root);