Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Базы Данных > select where `x` in (1,2,3) order by 1,2,3 ?


Автор: flashaa 28.6.2007, 01:03
Привет всем. У меня вопрос, у меня есть некий набор айдишников, допустим (1,2,3,4).
Я извлекаю все строки таблицы , в которых айди входит в это множество:

select * from user where `id` in (1,2,3,4) 

А вот теперь соственно вопрос: как мне упорядочить мои строки по порядку следования айдишников в запросе? 
При чем айдишники могут идти совершенно случайно, например (3,2,4,1) и при запросе я хочу получить ответ в таком порядке:

3 юзер с третим айдишником вася
2 юзер со вторым айдишником петя
4 юзер с четвертым айдишником котя
1 юзер с первым айдишником саша

Можно ли это вообще и если да, то как?

Автор: mishaSL 28.6.2007, 18:49
flashaa, нормального решения этого нет. А собственно зачем это? Лучше упорядочить строку с id которая идет в запрос. 

Автор: SelenIT 28.6.2007, 19:03
Как вариант,
Код

select * from user where `id` in (3,2,4,1) order by FIND_IN_SET(`id`, '3,2,4,1')

Автор: sTa1kEr 28.6.2007, 19:07
flashaa, упорядочнить можно следующим образом
Код

SELECT * FROM user WHERE `id` IN (1,2,3,4) ORDER BY FIELD(`id`, 3, 2, 4, 1);

Автор: flashaa 28.6.2007, 19:24
Cпасибо большое.
А упорядочить надо так потому сначала было несколько пользователей объединенных по общему полю (допустим user.active_room) - эти пользователи являлись участниками ОДНОЙ общей сущности,  и выборка этих пользователей осуществлялась значению ПОЛЬЗОВАТЕЛЬСКОГО поля.
Далее эта сущность становится недействительна, допустим закрывается некая комната переговоров с этими пользователями - 
cами пользователи высвобождаются  - их поле active_room становится нейтральным, но при этом необходимо сохранить для истории кто из пользователей был в этой комнате и в каком порядке на момент закрытия -
для этой цели я не придумал ничего другого как взять все АЙДИ этих пользователей, собрать в масиив, затем
в строку с разделителями "|" - получил  строку 4|3|2|1|5 и запихал ее в поле room.history закрытой комнаты.

Теперь, чтобы посмотреть кто из пользователей был в той комнате на момент закрытия, скрипт выполняет обратную операцию - получает этот набор айдишников из строки 4|3|2|1|5, 
далее  с помощью запроса 
Код

select * from user where `id` in (4,3,2,1,5) 

 получает эту выборку пользователей. Порядок важен , потому и возник данный вопрос.

Автор: mishaSL 28.6.2007, 20:28
ИМХО, тогда лучше сделать отдельную таблицу history с примерно такой структурой:
Код

history_id    int
room_id       int -  ID комнаты
user_id        int -  ID пользователя
order_user  int -  порядок пользователей
// т.д. другие необходимые поля


И выборка нужных данных будет гораздо проще и быстрее.

Автор: flashaa 28.6.2007, 23:09
не совсем понял, что от этого изменится? ведь все равно каждой комнате будет соотвествовать несколько (в общем случае) пользователей и чтобы уместить информацию об ОДНОЙ комнате в 1 строку, придется пихать эти айди опять таки в какое-то одно поле - аналогично моему решению, только с использованием доп. таблицы. Или может быть я вас не правильно понимаю?

Добавлено через 5 минут и 25 секунд
Кстати тут же позвольте спросить попутный вопрос: 
как лучше сделать? - много простых запросов к бд или один всеобмлющий запрос и его разбор в PHP.

Автор: Golda 28.6.2007, 23:21
flashaa, в примере mishaSL на одну комнату приходится столько записей, сколько в ней было пользователей. А их порядок определяется полем order_user. И умещать информацию об одной комнате в одну строку не приходится. Запрос:

Код

SELECT user.* 
FROM history AS h INNER JOIN user AS u ON h.user_id = u.id
WHERE h.room_id = 2
ORDER BY h.order_user


Добавлено через 3 минуты и 26 секунд
Как правило, один запрос сработает быстрее. Как в самой в базе, так и из-за того, что не придется по нескольку раз передавать данные между базой и скриптом.

Автор: sergejzr 28.6.2007, 23:42
Похожая ситуация:
происходит выборка постов на форуме, сортировка идёт например по времени. Идентификаторы результата сохраняются, чтобы не надо было каждый раз производить поиск, когда "листаешь" результаты. При этом при каждом "листании" приходится сортировать заного и конечно это лишняя работа. Тем более, что индекс по времени уже не использовать. 

Автор: sTa1kEr 29.6.2007, 00:00
Цитата(Golda @  28.6.2007,  23:21 Найти цитируемый пост)
Как правило, один запрос сработает быстрее. Как в самой в базе, так и из-за того, что не придется по нескольку раз передавать данные между базой и скриптом. 

Не всегда. К тому же часто (в отношении хостеров, правельнее сказать почти всегда) БД и веб-сервер находятся на одной физической машине и передача данных занимает минимальное время. В данном случае все зависит от конкретного запроса. Так например, объединение таблиц не по индексным полям займет очень много ресурсов. В тоже время выборка из одной таблицы по примари будет максимально быстрой.
Цитата

Some examples of queries that are very fast: 

SELECT COUNT(*) FROM tbl_name;

SELECT MIN(key_part1),MAX(key_part1) FROM tbl_name;

SELECT MAX(key_part2) FROM tbl_name
  WHERE key_part1=constant;

SELECT ... FROM tbl_name
  ORDER BY key_part1,key_part2,... LIMIT 10;

SELECT ... FROM tbl_name
  ORDER BY key_part1 DESC, key_part2 DESC, ... LIMIT 10;

Очень часто максимальной производительности можно достичь при помощи хранимых процедур. Т.е. все быстрые и маленькие выборки + некоторая логика и выборка из объемной таблицы по индексированным полям будет происходить в БД.

Автор: Golda 29.6.2007, 00:12
По поводу хранимых процедур согласна на 100%. Несколько раз в postgres ощутимо уменьшала время таким образом. Жаль, в MySQL недоступно

Автор: flashaa 29.6.2007, 00:26
А LIMIT сильно повышает производительность?

И после вот таких запросов 

Код

SELECT COUNT(*) FROM tbl_name;


Как извлекать результат? Так же как и без COUNT  ? т.е. mysql_result($r,$i,"user_name") ? Только теперь вместо набор юзернеймов будет одно число , которое показывает их количество?

Автор: sTa1kEr 29.6.2007, 00:43
Цитата(Golda @  29.6.2007,  00:12 Найти цитируемый пост)
Жаль, в MySQL недоступно 

Почему же? Начиная с 5ой версии доступно.
http://dev.mysql.com/doc/refman/5.0/en/stored-procedures.html
Цитата

А LIMIT сильно повышает производительность?

Да, если не используется сложная выборка.

Цитата(flashaa @  29.6.2007,  00:26 Найти цитируемый пост)
Как извлекать результат? Так же как и без COUNT  ? т.е. mysql_result($r,$i,"user_name") ?

Нет, в данном случае для поля надо задавать Alias.
Код

$r = mysql_query("SELECT COUNT(*) AS count_user_name FROM tbl_name");
$count = mysql_result($r,$i,"count_user_name");

Или обращаться по индексу.

Автор: sergejzr 29.6.2007, 12:38
Цитата(flashaa @  29.6.2007,  00:00 Найти цитируемый пост)
поэтому не буду делать новую тему, а спрошу здесь..

Модератор: Пожалуйста, один топик - один вопрос.!!

Автор: flashaa 29.6.2007, 14:50
Цитата(Golda @ 28.6.2007,  23:21)
flashaa, в примере mishaSL на одну комнату приходится столько записей, сколько в ней было пользователей. А их порядок определяется полем order_user. И умещать информацию об одной комнате в одну строку не приходится. Запрос:

Код

SELECT user.* 
FROM history AS h INNER JOIN user AS u ON h.user_id = u.id
WHERE h.room_id = 2
ORDER BY h.order_user


А если один пользователь был во многих комнатах? 
Тогда придётся скидывать каждогого пользователя из каждой комнаты в отдельную строку - очень много лишней информации

Автор: mishaSL 29.6.2007, 16:57
flashaa, эта информация не лишняя. Это называется нормализация структуры БД. С такой структурой БД можно будет одним запросом выбирать необходимую информацию, причем достаточно быстро.
Когда вы храните информацию в виде id1|id2|id3 в текстовом поле, то есть очевидные минусы:
 1. для выборки необходимо минимум два запроса: выбрать эту строку -> обработать результат -> выброть необходимые данные по       пользователям.
 2. Вы изначально не даете себе возможность хранить другие дополнительные данные, к примеру дату закрытия. Или есть появится необходимость в функции повторного открытия комната, то вам будет необходимо всего лишь  добавить в таблицу с историей поле дата и вы сможете хранить информацию о любом кол-ве закрытия комнат.
 3. с отдельной таблицей у вас появляется возможность бысторого вывода статистики, к примеру: в каких комнатах пользователь участвовал и т.д.

Автор: flashaa 29.6.2007, 17:29
Цитата(mishaSL @ 29.6.2007,  16:57)
1. для выборки необходимо минимум два запроса: выбрать эту строку -> обработать результат -> выброть необходимые данные по       пользователям.
 2. Вы изначально не даете себе возможность хранить другие дополнительные данные, к примеру дату закрытия. Или есть появится необходимость в функции повторного открытия комната, то вам будет необходимо всего лишь  добавить в таблицу с историей поле дата и вы сможете хранить информацию о любом кол-ве закрытия комнат.
 3. с отдельной таблицей у вас появляется возможность бысторого вывода статистики, к примеру: в каких комнатах пользователь участвовал и т.д.

1. Тут согласен
2. Дата закрытия, открытия - параметры самой комнаты, они хранятся в соотв. полях таблицы комнат.
Исходя из задачи: открывать закрытую комнату нельзя.
3. А вывод статистики в каких комнатах участвовал человек у меня происходит
Код

select * from room where (history like '%|idx|%') or (history like '%(idx|%') or 
(history like '%|idx)%')


 само поле history выглядит как "(id1,id2,id3)"  

Хотя вы меня переубедили, лучще сделаю лишнюю таблицу, сделаю бОльший запрос по вставке, зато все запросы по выборке упростятся а PHP-код упроститься и при вставке и при выборке.

Автор: mishaSL 29.6.2007, 17:32
Цитата(flashaa @  29.6.2007,  17:29 Найти цитируемый пост)
сделаю бОльший запрос по вставке, зато все запросы по выборке упростятся а PHP-код упроститься и при вставке и при выборке.

  smile 

Автор: flashaa 29.6.2007, 19:43
Цитата(sTa1kEr @ 29.6.2007,  00:43)
Нет, в данном случае для поля надо задавать Alias.
Код

$r = mysql_query("SELECT COUNT(*) AS count_user_name FROM tbl_name");
$count = mysql_result($r,$i,"count_user_name");

Или обращаться по индексу.

Что-то не работает  smile 

вот такой запрос:

Код

$r_ready_count=mysql_query("select count(*) as ready_users from users where `ready?`=1 
and `active_game`=".mysql_result($r_game,0,"id"));


вот так извлекаю:

Код

mysql_result($r_ready_count,0,"ready_users")


запрос проходит нормально. а вот извлечение выдает: 


Column count doesn't match value count at row 1

Автор: sTa1kEr 29.6.2007, 20:16
Цитата(flashaa @  29.6.2007,  19:43 Найти цитируемый пост)
Column count doesn't match value count at row 1 

Это ошибка бывает при вставке данных. Вы уверенны, что она происходит именно в этом запросе? Делайте проверки после каждого запроса.
Цитата(flashaa @  29.6.2007,  19:43 Найти цитируемый пост)
where `ready?`=1 

Вопросительный знак - это вы так колонку назвали? Оригинально smile

Автор: flashaa 29.6.2007, 22:19
Да, ошибка означает, что кол-во заявленных полей не соотвествует кол-ву значений, моя неправда..  smile 

Автор: Golda 30.6.2007, 09:20
sTa1kEr, спасибо за информацию о stored procedures в mysql. Упустила это событие

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)