我有下表
DROP TABLE IF EXISTS `test`.`foo`;
CREATE TABLE `test`.`foo` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
然后我尝试根据主键获取记录
SELECT * FROM foo f where f.id IN (2, 3, 1);
然后我得到以下结果
+----+--------+
| id | name |
+----+--------+
| 1 | first |
| 2 | second |
| 3 | third |
+----+--------+
3 rows in set (0.00 sec)
可以看出,结果按id排序。我想要实现的是按照我在查询中提供的顺序获取结果。鉴于此示例,它应该返回
+----+--------+
| id | name |
+----+--------+
| 2 | second |
| 3 | third |
| 1 | first |
+----+--------+
3 rows in set (0.00 sec)
答案 0 :(得分:83)
正如其他答案所提到的:您发布的查询并不了解您希望结果的顺序,只是您希望得到的结果。
要订购结果,我会使用ORDER BY FIELD():
SELECT * FROM foo f where f.id IN (2, 3, 1)
ORDER BY FIELD(f.id, 2, 3, 1);
FIELD的参数列表可以是可变长度。
答案 1 :(得分:23)
IN()
谓词中的值被视为一个集合,SQL查询返回的结果无法自动推断该集合的顺序。
通常,任何SQL查询的顺序都是任意的,除非指定带有ORDER BY
子句的订单。
您可以使用MySQL函数FIND_IN_SET()
来执行您想要的操作:
SELECT * FROM foo f where f.id IN (2, 3, 1)
ORDER BY FIND_IN_SET(f.id, '2,3,1');
请注意,FIND_IN_SET()
的list参数不是像IN()
的参数那样的可变长度列表。它必须是字符串文字或SET
。
关于性能的问题:我也很好奇,所以我针对我的StackOverflow数据副本尝试了FIND_IN_SET()
和FIELD()
方法:
VoteTypeId没有索引:
SELECT * FROM Votes ORDER BY FIND_IN_SET(VoteTypeId, '13,1,12,2,11,3,10,4,9,5,8,6,7');
3618992 rows in set (31.26 sec)
3618992 rows in set (29.67 sec)
3618992 rows in set (28.52 sec)
SELECT * FROM Votes ORDER BY FIELD(VoteTypeId, 13,1,12,2,11,3,10,4,9,5,8,6,7);
3618992 rows in set (37.30 sec)
3618992 rows in set (49.65 sec)
3618992 rows in set (41.69 sec)
使用VoteTypeId上的索引:
SELECT * FROM Votes ORDER BY FIND_IN_SET(VoteTypeId, '13,1,12,2,11,3,10,4,9,5,8,6,7');
3618992 rows in set (14.71 sec)
3618992 rows in set (14.81 sec)
3618992 rows in set (25.80 sec)
SELECT * FROM Votes ORDER BY FIELD(VoteTypeId, 13,1,12,2,11,3,10,4,9,5,8,6,7);
3618992 rows in set (19.03 sec)
3618992 rows in set (14.59 sec)
3618992 rows in set (14.43 sec)
结论:对于有限的测试,这两种方法都没有很大的优势。