【问题标题】:Maintaining order in MySQL "IN" query维护 MySQL“IN”查询中的顺序
【发布时间】:2010-12-10 13:03:15
【问题描述】:

我有下表

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)

【问题讨论】:

  • 在 MySQL 网站上查看这个答案:forums.mysql.com/read.php?97,210905,210918#msg-210918——我认为你会遇到麻烦,期待 in() 表现得像这样。 MySQL 不会真正对结果进行排序,除非您明确告知它,并且您的数据中没有任何内容可以按照您想要的方式对其进行排序。

标签: sql mysql


【解决方案1】:

正如另一个答案所提到的:您发布的查询与您希望结果的顺序无关,只是您希望获得哪些结果。

要对结果排序,我会使用 ORDER BY FIELD():

SELECT * FROM foo f where f.id IN (2, 3, 1)
ORDER BY FIELD(f.id, 2, 3, 1);

FIELD 的参数列表可以是可变长度的。

【讨论】:

  • 我很想看看这两种方法在按 ID 对行进行排序的一般情况下是否具有性能优势。我一直使用 FIELD(),但我看到了一些使用您发布的 FIND_IN_SET() 方法的代码示例。
  • 在 DB2 中有替代方案吗?
【解决方案2】:

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() 的列表参数不像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)

结论:在有限的测试中,这两种方法都没有太大的优势。

【讨论】:

  • 对于任意顺序列,CASE 'x' THEN 1,CASE 'y' THEN 2 更好
  • 感谢您澄清这一点 - 我选择了另一个答案,因为具有可变参数的 FIELD 对我来说似乎更直观。但我也对性能比较感兴趣。
  • 是的,我认为不会有太大差异,但感谢您的测试结果 :)
  • 有趣。因此,如果 VoteTypeId 上有索引,则没有很大的优势-没有索引,但是 FIND_IN_SET 似乎是更好的选择...感谢您的测试!
  • 我将添加免责声明,即我在只有 2GB RAM 的 Macbook 上运行这些测试,并且在测试之间可能没有足够的内存冷却时间。所以我的误差幅度可能有点高。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-13
  • 2021-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多