Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Выбор случайных записей

18K
11 января 2010 года
max_br
34 / / 10.12.2006
в головоу пришла очередная гениальная идея, и никак немогу понять где недостатки.

в основном записи выбираются методом: получаем общее число записей ( SELECT COUNT(*) FROM .. WHERE ..)
и дальше n запросов типа (SELECT * FROM .. WHERE .. LIMIT $rnd,1);
помоему 10 таких запросов - очень нагрузочное место и нагрузка, кажется, растет при увелечении кол-ва записей в таблице:
Цитата:
EXPLAIN SELECT * FROM `...` WHERE 'img'<>'' LIMIT 10,1

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE ir2_catalog_item ALL NULL NULL NULL NULL 86 Using where


т.к. записи меняются сравнительно редко(выбирать случайные надо намного чаще чем обновлять данные)у меня возникла мысля создать доб-таблицу c ключами удовлетворяющими условию и где PRIMARY KEY - последовательно меняется от 1 до кол-ва записей.
в результате для получения случайных записей из таблицы вышла такая ф-я:

Код:
function db_select_random($tbl,$num=1,$where='1',$key='id',$select='main.*',$left_join='',$rnd_prefix='rnd_')
  {
      global $cfg;
      if($cfg["dbconn"]===false) connect_to_db();
      if($cfg["dbconn"]===false) return false;

      $tbl_rnd=SQL.$rnd_prefix.md5($where);//имя таблицы для быстрого выбора случайных записей
      //при необходимости создаем данную таблицу
      $query=   "CREATE TABLE IF NOT EXISTS `$tbl_rnd` (
                     `key` INT NOT NULL auto_increment,
                     `id` INT NOT NULL DEFAULT 0,
                     PRIMARY KEY  (`key`))
                SELECT 0 AS `key`, `main`.`$key` AS `id`
                FROM `$tbl` AS `main`
                WHERE $where"
;
      @mysql_query($query);
      //кол-во записей в таблице
      $row=mysql_fetch_row(mysql_query("SELECT COUNT(*) FROM `$tbl_rnd`"));
      if(!$row)return false;
      $count=(int)$row[0];
      //генерация случайных ключей
      $keys=array();
      mt_srand();
      $i=1;
      while(($i<=$num)&&($i<=$count))
      {
          $k=mt_rand($i,$count);
          while(in_array($k,$keys))$k--;//для того чтобы ключи были уникальными
          $keys[]=$k;
      }
      if(count($keys)==0)return null;
      //построение ответа
      $query="SELECT $select FROM `$tbl_rnd` AS `rnd`
                LEFT JOIN `$tbl` AS `main` ON `main`.`$key`=`rnd`.`id`
                $left_join
                WHERE `rnd`.`key` IN ("
.implode(',',$keys).")";
      $res=db_query($query);
      $ret = array();
      while($row = mysql_fetch_array($res))
      {
          $ret[] = $row;
      }
      return $ret;
  }

здесь получается один жутко нагрузочный запрос по созданию новой таблицы, но он исполняется посути один раз после обновления записей.
и помоему максимально оптимальный запрос на выборку данных:
Цитата:
EXPLAIN SELECT main.* FROM `...` AS `rnd` LEFT JOIN `...` AS `main` ON `main`.`id`=`rnd`.`id` WHERE `rnd`.`key` IN (3,80,27)

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE rnd range PRIMARY PRIMARY 4 NULL 3 Using where
1 SIMPLE main eq_ref PRIMARY PRIMARY 4 w035.rnd.id 1


а т.к. в других проектах я такой метод не встречал, возникает вопрос: а каие недостатки данного метода я недосмотрел?

285
11 января 2010 года
Romik
479 / / 24.11.2002
По-чему бы не использовать для этого ORDER BY RAND() ?
18K
11 января 2010 года
max_br
34 / / 10.12.2006
ORDER BY RAND() - это, помоему, самое неудачное из всех возможных решений. при его использовании генерится дополнительная временная таблица с результатами rand() для каждой строчки. и потом производится упорядочнивание по этому полю. В результате при росте базы данных очень сильно падает производительность
92
11 января 2010 года
Тень Пса
2.2K / / 19.10.2006
сгенери сколько тебе нужно rand'омных ID (раз уж они последовательные) и сделай SELECT * WHERE id IN (..., ..., ..., ...)

сканает такой вариант? )
13
11 января 2010 года
RussianSpy
3.0K / / 04.07.2006
Гуглить пробовали?

http://habrahabr.ru/blogs/mysql/55864/
16K
11 января 2010 года
k0t
97 / / 23.04.2007
Цитата: RussianSpy
Гуглить пробовали?

http://habrahabr.ru/blogs/mysql/55864/



Который является ответом на этот топик с вариантом создания процедуры для MySQl http://habrahabr.ru/blogs/mysql/54176/

В обоих топиках самое интересное как всегда в комментариях.

18K
12 января 2010 года
max_br
34 / / 10.12.2006
Цитата:

сгенери сколько тебе нужно rand'омных ID (раз уж они последовательные) и сделай SELECT * WHERE id IN (..., ..., ..., ...)


именно для этих целей и генерилась дополнительная таблица в которой id идут последовательно.


Гуглить пробовал.
http://habrahabr.ru/blogs/mysql/55864/ - это раньше ненаходил.
наверно из за коментария про сентетический ключ (http://habrahabr.ru/blogs/mysql/54176/) и возникла такая мысль. Раньше использовал то что предложено в статье, но хочется найти метод пооптимальнее.

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


идею с ускорением ORDER BY RAND() пока еще полноценно продумать неуспел, но там возможна сложность если надо выбирать из нескольких разных наборов и зарание неизвестны размеры данных наборов и всей таблицы

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог