我在一個業務中采用了按月的分表策略,當查詢的條件跨月的時候,使用了union all匯總2個表的數據,並按插入時間倒序排列。查詢並不復雜,但是當執行的時候卻報錯了。
- SELECT * FROM `table_201604` ORDER BY `REPORT_TIME` DESC
- UNION ALL
- SELECT * FROM `table_201605` ORDER BY `REPORT_TIME` DESC
- [Err] 1221 - Incorrect usage of UNION and ORDER BY
報錯的原因,是不正確使用UNION和ORDER BY指令,為什么呢?經過一番查找資料,才知道MYSQL中UNION的語法是這樣子的
- substatement union [all] substatement union [all] substatement [order by-clause] [limit-clause]
注意:
1. 每一個子句可以使用()包圍,但是當第一個子句使用了()時,其它的子句都必須使用括號包圍。
2. 每一個字句可以包含WHERE,GROUP BY,HAVING,JOIN,LIMIT,但是不能使用ORDER BY,如果需要使用ORDER BY,必須使用()包圍子句。
接下來,做一些實驗,能更好地理解,創建表和數據的SQL語句如下:
- CREATE TABLE `union_a` (
- `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
- `T_NAME` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT ''COMMENT '所在表名稱',
- `NUMBER` int(11) NOT NULL DEFAULT '0' COMMENT '用於排序',
- PRIMARY KEY (`ID`)
- ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
- CREATE TABLE `union_b` LIKE `union_a`;
- INSERT INTO `union_a` VALUES ('1', 'a', '100');
- INSERT INTO `union_a` VALUES ('2', 'a', '28');
- INSERT INTO `union_a` VALUES ('3', 'a', '98');
- INSERT INTO `union_a` VALUES ('4', 'a', '19');
- INSERT INTO `union_a` VALUES ('5', 'a', '999');
- INSERT INTO `union_b` VALUES ('1', 'b', '23');
- INSERT INTO `union_b` VALUES ('2', 'b', '76');
- INSERT INTO `union_b` VALUES ('3', 'b', '32');
- INSERT INTO `union_b` VALUES ('4', 'b', '43');
- INSERT INTO `union_b` VALUES ('5', 'b', '11');
一、關於LIMIT
執行以下SQL語句
- SELECT * FROM `union_a` LIMIT 1
- UNION ALL
- SELECT * FROM `union_b` LIMIT 1
可能第一眼看到這個SQL的時候,會想到將會返回2條記錄,但是,結果卻是只返回1條記錄。回憶UNION的語法,最后一個LIMIT 1其實是對UNION之后的結果集取1條記錄。
二、關於ORDER BY
我的需求是這樣的,`union_a`和`union_b`表按NUMBER字段升序排列,最后將2個結果集合並,你可能想到的SQL會是這個樣子
- SELECT * FROM `union_a` ORDER BY `NUMBER`
- UNION ALL
- SELECT * FROM `union_b` ORDER BY `NUMBER`
前面也說過,這個SQL語句是有語法錯誤的,但是,可以嘗試這樣,使用()將每個子句包圍
- (SELECT * FROM `union_a` ORDER BY `NUMBER`)
- UNION ALL
- (SELECT * FROM `union_b` ORDER BY `NUMBER`)
SQL語句順利執行了,但是,結果卻不是我們想要的。看前5條記錄,`union_a`表中的數據並沒有按照NUMBER字段升序排列
查閱MYSQL的官方文檔,其中包含對UNION中使用ORDER BY的說明:
對UNION中的子句應用ORDER BY是無效的,ORDER BY只能用於UNION后的整個結果集。如果需要對子句應用ORDER BY,必須添加LIMIT。正確的SQL如下
- (SELECT * FROM `union_a` ORDER BY `NUMBER` LIMIT 5)
- UNION ALL
- (SELECT * FROM `union_b` ORDER BY `NUMBER` LIMIT 5)
參考鏈接:
https://dev.mysql.com/doc/refman/5.7/en/union.html
http://stackoverflow.com/questions/6732661/incorrect-usage-of-union-and-order-by