Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/topic.php on line 737
Форумы портала PHP.SU :: случайная запись в MySQl [2]

 PHP.SU

Программирование на PHP, MySQL и другие веб-технологии
PHP.SU Портал     На главную страницу форума Главная     Помощь Помощь     Поиск Поиск     Поиск Яндекс Поиск Яндекс     Вакансии  Пользователи Пользователи


 Страниц (2): « 1 [2]   

> Без описания
evgenijj
Отправлено: 14 Апреля, 2007 - 11:02:19
Post Id



Участник


Покинул форум
Сообщений всего: 1212
Дата рег-ции: Авг. 2006  
Откуда: Москва


Помог: 10 раз(а)




Возвращаюсь к теме:
PHP:
скопировать код в буфер обмена
  1.  
  2. <?PHP
  3. function gettime()
  4. {
  5.     $part_time = explode( ' ', microtime() );
  6.     $real_time = $part_time[1].substr( $part_time[0], 1 );
  7.     return $real_time;
  8. }
  9.  
  10. // Получаем случайную запись - первый вариант
  11. $start_time = gettime();
  12. $query = "SELECT id_prd, code, title, info, price, currency, image
  13.                   FROM ".TABLE_PRODUCTS."
  14.                   WHERE leader=1 AND active=1 ORDER BY RAND() LIMIT 1";
  15. $res = db_query( $query );
  16. if ( db_num_rows( $res ) > 0 )
  17. {
  18.         $leader = db_fetch_array( $res );
  19.         echo "Код: ".$leader["code"]."<br/>";
  20.         echo "Наименование: ".$leader["title"]."<br/>";
  21.         echo "Цена: ".number_format($leader["price"], 2, '.', '')."&nbsp;".CURRENCY_CODE."<br/>";
  22. }
  23. $stop_time = gettime();
  24. $dif_time = bcsub( $stop_time, $start_time, 6 );
  25. echo "<p >Время выполнения: ".$dif_time."</p>";
  26.  
  27. // Получаем случайную запись - второй вариант
  28. $start_time = gettime();
  29. $query = "SELECT id_prd
  30.                   FROM ".TABLE_PRODUCTS."
  31.                   WHERE leader=1 AND active=1";
  32. $res = db_query( $query );
  33. while( $id = db_fetch_array($res) ) $available_ids[] = $id["id_prd"];
  34. $rand = rand( 0 , (count($available_ids)-1) );
  35. $id = $available_ids[$rand];
  36. $query = "SELECT id_prd, code, title, info, price, currency, image
  37.                   FROM ".TABLE_PRODUCTS."
  38.                   WHERE id_prd=".$id;
  39. $res = db_query( $query );
  40. if ( db_num_rows( $res ) > 0 )
  41. {
  42.         $leader = db_fetch_array( $res );
  43.         echo "Код: ".$leader["code"]."<br/>";
  44.         echo "Наименование: ".$leader["title"]."<br/>";
  45.         echo "Цена: ".number_format($leader["price"], 2, '.', '')."&nbsp;".CURRENCY_CODE."<br/>";
  46. }
  47. $stop_time = gettime();
  48. $dif_time = bcsub( $stop_time, $start_time, 6 );
  49. echo "<p >Время выполнения: ".$dif_time."</p>";
  50.  
  51. ?>
  52.  

Результаты (таблица содержит около 6000 записей):

Код: 1007
Наименование: Датчик инерционный магнитоконтактный ДИМК
Цена: 20.70 RUB
Время выполнения: 0.043781

Код: 1016
Наименование: Извещатель охранный ручной точечный электроконтактный ИО 101-1 (КНС-1 исп.А)
Цена: 147.50 RUB
Время выполнения: 0.006133
-------------------------------- -------------------------------- ---------------------
Код: 1001
Наименование: Извещатель охранный точечный магнитоконтактный ИО 102-2 (СМК-1)
Цена: 16.65 RUB
Время выполнения: 0.036725

Код: 1016
Наименование: Извещатель охранный ручной точечный электроконтактный ИО 101-1 (КНС-1 исп.А)
Цена: 147.50 RUB
Время выполнения: 0.000661
-------------------------------- -------------------------------- ----------------------
Код: 2006
Наименование: Извещатель охранный объемный радиоволновый "Аргус-2"
Цена: 796.50 RUB
Время выполнения: 0.024019

Код: 2043
Наименование: Извещатель охранный поверхностный звуковой "Стекло-3" (ИО 329-4)
Цена: 605.34 RUB
Время выполнения: 0.000361

Второй вариант явно лучше. Так что переделываю с первого на второй.
 
 Top
Ivanezko
Отправлено: 13 Октября, 2007 - 12:22:45
Post Id


Новичок


Покинул форум
Сообщений всего: 10
Дата рег-ции: Окт. 2007  


Помог: 0 раз(а)




valenok пишет:
Давай тогда ещё раз.

Что у нас происходит когда мы выполняем следующий запрос: ?
SELECT * FROM `table` WHERE `char`='A' AND `num`=1 ORDER BY RAND() LIMIT 1


К сожалению, MySQL генерит дополнительное поле (виртуальное) и генерит случайные числа для каждой записи.
Потом проводит сортировку по этому полю - ВСЕЙ таблицы.
и только потом выбирает количество записей указанное в LIMIT.
Если записей в таблице менее тысячи - можно и ORDER BY RAND() использовать.
У меня их 300к+. Запрос отрабатывает около 500 секунд.
Грустно это все. Уже неделю ищу решение.
Вот хорошая статья: http://jan[dot]kneschke[dot]de/projects/[dot][dot][dot]ql/order-by-rand , но простой способ дает у меня для сложного запроса с джойнами около 1 секунды для 1 результата (а мне их надо 15) - итого 15 секунд.
а способ с введением доптаблицы для holes мне не нравится.

В результате пришел к нечестной выборке:
1) взять кол-во записей удовлетворяющих запросу - $total
2) ПХПой сгенерить рандомное "окно" в промежутке 0<$randnum<($total-1000)
3) запрос LIMIT $randnum, 1000
4) shuffle($res)
5) array_slice($res, 0, сколько_надо_в_результате);
Этот метод дает в общем-то рандомный результат и работает очень быстро по сравнению с другими (на все про все около секунды).
Правда не очень честный - все результаты находятся в пределах "окна" в 1к записей.

Если кто-то найдет более честный способ - пожалуйста отпишите буду очень признателен.
 
 Top
EuGen Администратор
Отправлено: 13 Октября, 2007 - 20:29:39
Post Id


Профессионал


Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




Оговорюсь сразу: пост для математически подкованных людей
Не поленился тут проанализировать то, что народ понаписал.
Пришел к выводу - как правило, неэффективность того или иного метода состоит в том, что для выборки q случайных строк их исходного множества мощностью p приходится генерировать q случайных чисел, да еще потом с ними что то делать (как в случае с ORDER BY rand())
Однако, в принципе ничто не мешает один раз генерировать случайное число.
Так как всех возможных вариантов выборок множества строк мощностью q из множества мощностью p есть
PHP:
скопировать код в буфер обмена
  1.  
  2. A(p,q)=(p!/(p-q)!)=(p-q+q)x(p-q+2)x...x(p) //назовем A(p,q) как R
  3.  

То, генерируя число в диапазоне от 1 до R, можно в принципе построить по нему множество из q строк. Иначе говоря, все варианты этих множеств можно занумеровать, а потом по числу 0<s<R+1 выдавать множество, чьим номером это s является.
Тут две трудности: во-первых, построить нумерацию этих множеств. Во-вторых, собственно с минимальными затратами "восстановить множество". Ведь в терминах sql это будет выглядеть примерно так:
WHERE pk IN (построенное множество)
(pk-некоторая колонка-нумератор)
Задача состоит в том, чтобы по заданному 0<s<R+1 построить набор из q функций F, которые будут для этого s выдавать номера строк, таким образом, множество из результатов этих q функций даст нам требуемое.
То есть
F1(s)=K1
F2(s)=K2
...
Fq(s)=Kq
И Ki!=Kj при i!=j
Пока что я уперся в подбор этих функций, они должны быть простыми (иначе весь метод нафиг не нужен) и в то же время охватывать для всех s все R комбинаций.
Если кого-то заинтересовал этот метод и есть идеи по построению F - вместе добьем эту задачу
В частности, для одной строки метод дает простой ответ - генерируем число от 1 до p и берем эту строку как решение:
PHP:
скопировать код в буфер обмена
  1.  
  2. $res=mysql_query("SELECT *...");//множество, мощность которого считаем, то есть p
  3. $p=mysql_num_rows($res);
  4. //либо можно при помощи count() считать наше p
  5. $rnd=rand(1,$p);
  6. $result=mysql_query("SELECT * FROM our_table WHERE our_pk=$rnd");
  7. //либо $result=mysql_query("SELECT * FROM our_table LIMIT $rnd,1");
  8. $solution=mysql_fetch_array($result);
  9.  


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
Whitex
Отправлено: 22 Октября, 2007 - 19:34:40
Post Id


Новичок


Покинул форум
Сообщений всего: 1
Дата рег-ции: Окт. 2007  


Помог: 0 раз(а)




незнаю было или нет такое предложение:

$r = mysql_query("SELECT COUNT(*) FROM `TABLE`");
$c = mysql_fetch_array($r);
$k = rand(0,$c[0]-1);
$r = mysql_query("SELECT * FROM `TABLE` LIMIT ".$k.",1");

 
 Top
Ivanezko
Отправлено: 22 Октября, 2007 - 20:15:13
Post Id


Новичок


Покинул форум
Сообщений всего: 10
Дата рег-ции: Окт. 2007  


Помог: 0 раз(а)




Whitex пишет:
незнаю было или нет такое предложение:
$r = mysql_query("SELECT * FROM `TABLE` LIMIT ".$k.",1");

"SELECT * FROM `TABLE` LIMIT ".$k.",1" - парадокс, но это небыстрый запрос, и скорость падает (sic!) при увеличении $k.
т.е. если выбирать 1 запись - ваш вариант вполне подходит.
А если 10 или 20 ?
 
 Top
EuGen Администратор
Отправлено: 22 Октября, 2007 - 22:52:20
Post Id


Профессионал


Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




Такой вариант не подойдет, потому что нет никакой гарантии в том, что поле первичного ключа будет представлять собой правильную последовательность 1,2,3, ..., N, ... "без дыр"


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
Ivanezko
Отправлено: 23 Октября, 2007 - 08:01:01
Post Id


Новичок


Покинул форум
Сообщений всего: 10
Дата рег-ции: Окт. 2007  


Помог: 0 раз(а)




EuGen пишет:
Такой вариант не подойдет, потому что нет никакой гарантии в том, что поле первичного ключа будет представлять собой правильную последовательность 1,2,3, ..., N, ... "без дыр"

Удивил.
Не надо путать
LIMIT $k
и
WHERE id=$k
 
 Top
EuGen Администратор
Отправлено: 23 Октября, 2007 - 09:24:22
Post Id


Профессионал


Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




Хм.. а я как раз и имел WHERE, ибо вариант с LIMIT весьма и весьма нерадостен (mysql делает full scan)


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
Ivanezko
Отправлено: 23 Октября, 2007 - 19:03:03
Post Id


Новичок


Покинул форум
Сообщений всего: 10
Дата рег-ции: Окт. 2007  


Помог: 0 раз(а)




По моим тестам не фул скан а только до нужной записи
т.е. лимит 10,1 - быстро
лимит 1000,1 - средне
лимит 1000000,1 - медленно
 
 Top
EuGen Администратор
Отправлено: 24 Октября, 2007 - 09:53:36
Post Id


Профессионал


Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




Вот смотрите:
1)Таблица 105000 строк с небольшим.
2)делается запрос:
PHP:
скопировать код в буфер обмена
  1.  
  2. explain select * from account_log_2007_10_23 limit 100;
  3.  

имеем:
PHP:
скопировать код в буфер обмена
  1.  
  2. mysql> explain select * from account_log_2007_10_23 limit 100;
  3. +----+-------------+------------------------+------+---------------+------+---------+------+--------+-------+
  4. | id | select_type | table                  | type | possible_keys | key  | key_len | ref  | rows   | Extra |
  5. +----+-------------+------------------------+------+---------------+------+---------+------+--------+-------+
  6. |  1 | SIMPLE      | account_log_2007_10_23 | ALL  | NULL          | NULL | NULL    | NULL | 105251 |       |
  7. +----+-------------+------------------------+------+---------------+------+---------+------+--------+-------+
  8.  

обратите внимание на столбец "rows" - и Вам сразу станет ясно как оно делается.


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
Ivanezko
Отправлено: 24 Октября, 2007 - 11:47:29
Post Id


Новичок


Покинул форум
Сообщений всего: 10
Дата рег-ции: Окт. 2007  


Помог: 0 раз(а)




Очевидно вы правы.
 
 Top
Страниц (2): « 1 [2]
Сейчас эту тему просматривают: 0 (гостей: 0, зарегистрированных: 0)
« Программирование на PHP »


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



Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS  RSS

 
Powered by ExBB FM 1.0 RC1. InvisionExBB