维护MySQL“IN”查询中的顺序

时间:2009-10-27 15:43:17

标签: sql mysql

我有下表

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)

2 个答案:

答案 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)

结论:对于有限的测试,这两种方法都没有很大的优势。