Поддержание порядка в запросе «IN» MySQL - PullRequest
44 голосов
/ 27 октября 2009

У меня есть следующая таблица

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 ]

83 голосов
/ 27 октября 2009

Как уже упоминалось в другом ответе: в опубликованном вами запросе ничего не говорится о том, в каком порядке вы хотите получать результаты, а только о том, какие результаты вы хотите получить.

Чтобы упорядочить результаты, я бы использовал ORDER BY FIELD ():

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

Список аргументов FIELD может быть переменной длины.

23 голосов
/ 27 октября 2009

Значения в предикате 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.


Ответы на вопросы о производительности: мне тоже любопытно, поэтому я попробовал оба метода FIND_IN_SET() и FIELD() для моей копии данных StackOverflow:

Без индекса на 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)

Вывод: при ограниченном тестировании нет большого преимущества ни для одного из методов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...