Помогите выпольнить SQL запрос

#16 23 августа 2013 в 22:17

Его можно применять

Можно все, но тут лучше 7 раз подумать.

Я пока только учусь и по этому… могу использовать только в одном месте

Ну как бы… если запрос выполняется долго — лучше его не использовать… Имхо.

еще один вариант… а таблица лишь вернет записи тех значений которые передадут в запросе

Не будет работать так как id последовательность не равномерная.

11 простых запросов выполнятся быстрее.
#17 24 августа 2013 в 00:30

Можно все, но тут лучше 7 раз подумать.

верно, и если использовать то точно не по всей таблице, а предварительно сократить область.

Не будет работать так как id последовательность не равномерная.

Пример приводился для таблиц с наличием ид с инкрементом. Для автора нужно работать с другими столбцами, т.е. подготовить все условия для простых запросов.
#18 24 августа 2013 в 00:37

Пример приводился для таблиц с наличием ид с инкрементом


не могу себе представить таблицу с равномерным инкрементом. в моей практике это редкость crazy
#19 24 августа 2013 в 00:54
Мне очень часто встречаются smile
Суть все равно одна, человеку желательно прийти к простым запросам без перебора огромных количеств данных)
#20 24 августа 2013 в 08:26
Smith, Александр, так посоветуйте на указанных выше примерах, как переделать запросы, чтобы быстрее работало 😊 Это будет наглядный пример, как надо писать запросы, а как нет. Это будет всем интересно.
#21 24 августа 2013 в 16:08

так посоветуйте на указанных выше примерах, как переделать запросы


Информации в интернете достаточно чтобы выбрать один из способов. Можно и свой придумать.)

Но раз Вам так интересно — давайте проведем небольшой эксперимент.

Говорю сразу что тесты не претендуют на точность.

Характеристики машины
Ocь: Ubuntu 13.04 64bit
Проц: Intel® Core™ i5-3570K CPU @ 3.40GHz × 4
Память: 8 G

Часть 1. Подготовка данных.

Создадим тестовых пользователей. Думаю хватит один-два миллиона.
Потом что-то мне надоело ждать — прервал работу скрипта. Получилось 1,672,354 юзеров.

  1.  
  2.  
  3. $inDB = cmsDatabase::getInstance();
  4.  
  5. for ($i = 1; $i < 2000000; $i++) {
  6.  
  7. $item = array(
  8. 'nickname' => "user_{$i}",
  9. "login" => "user_{$i}",
  10. );
  11. $inDB->insert('cms_users', $item);
  12. }
  13.  
  14.  

Часть 2. Логика.

Я просо хочу проверить теорию — что 10 запросов к базе данных отработают быстрее чем
один запрос с ORDER BY RAND().

Кодим...

  1.  
  2.  
  3. class RandomUser {
  4.  
  5. private $db;
  6. private $limit;
  7. private $usersTableName = 'cms_users';
  8. private $users = array();
  9.  
  10. public function __construct($limit) {
  11.  
  12. $this->db = cmsDatabase::getInstance();
  13. $this->limit = $limit;
  14. }
  15.  
  16. public function getUsers() {
  17.  
  18. $usersIds = array();
  19. $maxId = $this->getMaxId();
  20. for ($i = 0; $i < $this->limit; $i++) {
  21.  
  22. // получаем рандомный Id
  23. $randomId = mt_rand(0, $maxId);
  24.  
  25. // составляем sql запрос
  26. $sql = "SELECT id, nickname FROM {$this->usersTableName} AS u WHERE id >= {$randomId}";
  27.  
  28. // одного пользователя можно достать только 1 раз
  29. $sql .= $usersIds ? ' AND id NOT IN (' . implode(',', $usersIds) . ')' : '';
  30. $sql .= " ORDER BY id ASC LIMIT 1";
  31.  
  32. $user = $this->db->query($sql)->fetch_object();
  33.  
  34. $usersIds[] = $user->id;
  35.  
  36. $this->users[] = $user;
  37. }
  38.  
  39. return $this->users;
  40. }
  41.  
  42. public function getUsersBadMethod() {
  43.  
  44. $sql = "SELECT id, nickname FROM {$this->usersTableName} ORDER BY RAND() LIMIT {$this->limit}";
  45. $result = $this->db->query($sql);
  46.  
  47. while($user = $this->db->fetch_assoc($result)) {
  48. $this->users[] = $user;
  49. }
  50.  
  51. return $this->users;
  52. }
  53.  
  54. /**
  55.   * @return int - максимальный (последний) Id пользователя
  56.   */
  57. private function getMaxId() {
  58.  
  59. $sql = "SELECT MAX(id) as maxId FROM {$this->usersTableName}";
  60. return $this->db->query($sql)->fetch_object()->maxId;
  61. }
  62. }
  63.  
  64.  

И еще один класс.
Хелпер для подсчета времени выполнения скрипта.

  1.  
  2.  
  3. class Timer {
  4.  
  5. private $startTime;
  6.  
  7. private function getTime() {
  8.  
  9. list($usec, $seconds) = explode(' ', microtime());
  10. return ((float) $usec + (float) $seconds);
  11. }
  12.  
  13. function start() {
  14.  
  15. $this->startTime = $this->getTime();
  16. }
  17.  
  18. function end() {
  19.  
  20. return ($this->getTime() - $this->startTime);
  21. }
  22. }
  23.  
  24.  

Ну вот и все. Работаем...

  1.  
  2.  
  3. $timer = new Timer();
  4.  
  5. $timer->start();
  6. $randomUser = new RandomUser(10);
  7. $users = $randomUser->getUsers();
  8. $time1 = $timer->end();
  9.  
  10. $timer->start();
  11. $randomUser = new RandomUser(10);
  12. $users = $randomUser->getUsersBadMethod();
  13. $time2 = $timer->end();
  14.  
  15.  
  16. echo "Time 1: " . number_format($time1, 4) . ' sec';
  17. echo '<br />';
  18. echo "Time 2: " . number_format($time2, 4) . ' sec';
  19.  
  20.  


Time 1 — десять запросов
Time 2 — один не очень хороший (имхо) запрос



Time 1: 0.0030 sec
Time 2: 2.4021 sec

Time 1: 0.0026 sec
Time 2: 2.6235 sec


и т.д.
#22 24 августа 2013 в 16:38
Интересно получилось scratch
И похоже этот метод будет работать более правильно в плане выборки, ибо мой вариант с UNION меня почему-то смущает из-за LIMIT в первом запросе (мне кажется, рандом в этом случае исключается), хотя надо посмотреть, что сначала отрабатывает, рандом или лимит crazy...
Спасибо большое за пример 😊
#23 24 августа 2013 в 16:46

получаем рандомный Id

Александр
Нет проверки, что этот ид существует, но думаю, что выполнив 1000 запросов мы уж наверняка получим 10 случайных)
#24 24 августа 2013 в 16:55

Нет проверки, что этот ид существует, но думаю


Смотрите пример.
Нам и не надо чтоб этот id существовал.
Нам главное получить число в диапазоне от
0 до MAX_ID а в запросе мы достаем записи которые находятся рядом с этим ID
  1.  
  2. WHERE id >= {$randomId}
  3.  
Тем не менее этот класс не совсем пригоден для работы на реальном проекте.
В методе getUsers запрос

  1.  
  2. $sql = "SELECT id, nickname FROM {$this->usersTableName} AS u WHERE id >= {$randomId}";
  3.  
может и не достать пользователя — в таком случае нужно искать "вниз" от рандомного Id


  1.  
  2. ..WHERE id < {$randomId}
  3.  
#25 24 августа 2013 в 17:07
Тогда может быть сделаю сортировку по (date_log) дата последней авторизации а не RAND()
Мне главное каждый раз получить разных юзеров, хотя разница будет зависит от посещение сайта, для сайтов которых посещают 20-30 юзера в день — одни и те же юзеры всегда будут отображаться sadили же в админке компонента сделать опцию сортировка по дате или случайно…
#26 24 августа 2013 в 17:13
да я бы делал 12 запросов и не переживал

приходилось работать с drupal. там по 300 — 500 запросов на одну страницу и ниче так) все работает.
и ресурс довольно посещаемый.
#27 24 августа 2013 в 17:31
Александр, Ок спасибо всем за советы.
Вы не можете отвечать в этой теме.
Войдите или зарегистрируйтесь, чтобы писать на форуме.
Используя этот сайт, вы соглашаетесь с тем, что мы используем файлы cookie.