元のクエリ
SELECT * FROM Board ORDER by root DESC, path ASC LIMIT 0,100;
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>
次に、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>
試してみる!!!
警告
ルートが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-0406:54EDT
私の答えについての@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の3つの列を取得する必要があります。したがって、それらはインデックスに含まれている必要があります。もちろん、インデックスのサイズを大きくするとトレードオフになります。ボードテーブルが非常に大きい場合、検索を高速化できればスペースを気にしない人もいます。ルートパスインデックスがちょうど(rootinv、path)の場合、すべてのインデックス範囲スキャンには、残りの列のテーブルへのrefルックアップが伴います。これが私が選んだ理由ですALTER TABLE BoardDisplayOrder ADD INDEX rootpathid (rootinv,path,id,root);