自從誕生以來,MariaDb(MySQL)索引有一個限製:它們不能降序(create index desc)。是的,ALTER TABLE和CREATE INDEX可以包含ASC和DESC關鍵字,這在MySQL中完全合法,但這並不意味著下降索引被創建。也就是Mysql支持索引desc這樣的語法,但是實際上並不支持這樣的功能,比如:order by column _name desc並不能使用索引。這是一個眾所周知的MySQL功能:當它不能做某事時,它隻是假裝。嗯…有人認為這是一個功能,但我認為這是一個BUG(一個錯誤是一個意想不到的行為),那我們能做什麽呢?
缺少對降序索引的支持隻是一個問題,當我們需要創建一個索引,其中至少有一列升序,並且至少有一列是降序。例如,MySQL假裝理解以下語句,但是生成的索引不太可能有助於我們:
CREATE INDEX my_index ON my_table (my_column ASC, your_column DESC);
ORDER BY my_column ASC,your_column DESC將無法使用此索引。
MariaDB有一個功能,為缺少降序索引提供了一個很好的解決方法:PERSISTENT列。如果您不了解它們,請先查看MariaDB KnowledgeBase。
PERSISTENT列如何幫助我們創建混合order索引?如果你還沒有猜到,我會告訴你的。假設我們有一列A,我們希望它成為ASC + DESC索引的一部分。我們需要創建一個PERSISTENT列rev_A,其值是根據A中的值計算的。這裏重要的是rev_A值的順序與A是相反的。這是一個例子:
CREATE TABLE `exam_scores`
(
`id` INT SIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
, `student_name` VARCHAR(200) NOT NULL
, `exam_code` CHAR(1)
, `exam_score` SMALLINT UNSIGNED NOT NULL
, `exam_date` DATETIME NOT NULL
-- auxiliary columns
, `rev_exam_code` TINYINT UNSIGNED AS (255 - ORD(exam_code)) PERSISTENT
, `rev_exam_score` SMALLINT SIGNED AS (0 - exam_score) PERSISTENT
, `rev_exam_date` SMALLINT UNSIGNED AS (DATEDIFF('2050-01-01', exam_date)) PERSISTENT
-- mixed indexes
, INDEX `desc_exam_code` (`rev_exam_code`, `student_name`, `exam_code`)
, INDEX `desc_exam_score` (`rev_exam_score`, `student_name`)
, INDEX `desc_exam_date` (`rev_exam_date`, `student_name`, `exam_date`)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = 'utf8'
;
-- this is needed because some rev* columns return negative values
SET @@global.sql_mode := CONCAT(@@global.sql_mode, ',NO_UNSIGNED_SUBTRACTION');
SET @@session.sql_mode := @@global.sql_mode;
INSERT INTO `exam_scores`
(`student_name`, `exam_code`, `exam_score`, `exam_date`)
VALUES
('Anthony Stark', 'F', 100, '1994-01-01')
, ('Klark Kent', 'G', 60, '2000-01-01')
, ('Corto Maltese', 'A', 50, '2007-01-01')
, ('Tin Tin', 'Z', 900, '2005-01-01')
, ('Philip Mortimer', 'C', 20, '2000-01-01')
, ('Dago', 'M', 80, '2004-01-01')
;
是的,這張表設計得很爛。你想說它不是好的關係表等等,但是我們在談論在MariaDB中的混合order索引,而不是關係理論。
現在很清楚嗎?但願如此。我們來看一個示例查詢。我們想列出學生:最近的考試,如果兩個日期相同,按字母排序。
MariaDB [test]> EXPLAIN SELECT `student_name`, `exam_date`
-> FROM `exam_scores`
-> ORDER BY `rev_exam_date` ASC, `student_name` ASC;
+------+-------------+-------------+-------+---------------+----------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------------+-------+---------------+----------------+---------+------+------+-------------+
| 1 | SIMPLE | exam_scores | index | NULL | desc_exam_date | 613 | NULL | 7 | Using index |
+------+-------------+-------------+-------+---------------+----------------+---------+------+------+-------------+
1 row in set (0.00 sec)
desc_exam_score索引表明:在索引中包含原始列並不是必須的:
MariaDB [test]> EXPLAIN SELECT `student_name`, -`rev_exam_score` FROM `exam_scores` ORDER BY `rev_exam_score` ASC, `student_name` ASC;
+------+-------------+-------------+-------+---------------+-----------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------------+-------+---------------+-----------------+---------+------+------+-------------+
| 1 | SIMPLE | exam_scores | index | NULL | desc_exam_score | 605 | NULL | 6 | Using index |
+------+-------------+-------------+-------+---------------+-----------------+---------+------+------+-------------+
1 row in set (0.00 sec)
-column_name
顯然是一個快速的獲得真實值的操作。
示例表格顯示了如何為數字,日期和ASCII 1字符串創建一個反向的索引部分。多字符ASCII字符串也很簡單。我不知道如何創建一個反轉的Unicode列。如果找到方法,請在文章下麵留下評論。
本文翻譯自:https://falseisnotnull.wordpress.com/2014/04/25/descending-indexes-in-mariadb/