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]
Покинул форум
Сообщений всего: 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к записей.
Если кто-то найдет более честный способ - пожалуйста отпишите буду очень признателен.
EuGen
Отправлено: 13 Октября, 2007 - 20:29:39
Профессионал
Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007 Откуда: Berlin
Помог: 707 раз(а)
Оговорюсь сразу: пост для математически подкованных людей
Не поленился тут проанализировать то, что народ понаписал.
Пришел к выводу - как правило, неэффективность того или иного метода состоит в том, что для выборки q случайных строк их исходного множества мощностью p приходится генерировать q случайных чисел, да еще потом с ними что то делать (как в случае с ORDER BY rand())
Однако, в принципе ничто не мешает один раз генерировать случайное число.
Так как всех возможных вариантов выборок множества строк мощностью q из множества мощностью p есть
A(p,q)=(p!/(p-q)!)=(p-q+q)x(p-q+2)x...x(p)//назовем A(p,q) как R
То, генерируя число в диапазоне от 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 и берем эту строку как решение:
----- Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
Whitex
Отправлено: 22 Октября, 2007 - 19:34:40
Новичок
Покинул форум
Сообщений всего: 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");
Ivanezko
Отправлено: 22 Октября, 2007 - 20:15:13
Новичок
Покинул форум
Сообщений всего: 10
Дата рег-ции: Окт. 2007
Помог: 0 раз(а)
Whitex пишет:
незнаю было или нет такое предложение:
$r = mysql_query("SELECT * FROM `TABLE` LIMIT ".$k.",1");
"SELECT * FROM `TABLE` LIMIT ".$k.",1" - парадокс, но это небыстрый запрос, и скорость падает (sic!) при увеличении $k.
т.е. если выбирать 1 запись - ваш вариант вполне подходит.
А если 10 или 20 ?
EuGen
Отправлено: 22 Октября, 2007 - 22:52:20
Профессионал
Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007 Откуда: Berlin
Помог: 707 раз(а)
Такой вариант не подойдет, потому что нет никакой гарантии в том, что поле первичного ключа будет представлять собой правильную последовательность 1,2,3, ..., N, ... "без дыр"
----- Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
Ivanezko
Отправлено: 23 Октября, 2007 - 08:01:01
Новичок
Покинул форум
Сообщений всего: 10
Дата рег-ции: Окт. 2007
Помог: 0 раз(а)
EuGen пишет:
Такой вариант не подойдет, потому что нет никакой гарантии в том, что поле первичного ключа будет представлять собой правильную последовательность 1,2,3, ..., N, ... "без дыр"
Удивил.
Не надо путать
LIMIT $k
и
WHERE id=$k
EuGen
Отправлено: 23 Октября, 2007 - 09:24:22
Профессионал
Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007 Откуда: Berlin
Помог: 707 раз(а)
Хм.. а я как раз и имел WHERE, ибо вариант с LIMIT весьма и весьма нерадостен (mysql делает full scan)
----- Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
Ivanezko
Отправлено: 23 Октября, 2007 - 19:03:03
Новичок
Покинул форум
Сообщений всего: 10
Дата рег-ции: Окт. 2007
Помог: 0 раз(а)
По моим тестам не фул скан а только до нужной записи
т.е. лимит 10,1 - быстро
лимит 1000,1 - средне
лимит 1000000,1 - медленно
EuGen
Отправлено: 24 Октября, 2007 - 09:53:36
Профессионал
Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007 Откуда: Berlin
Помог: 707 раз(а)
Вот смотрите:
1)Таблица 105000 строк с небольшим.
2)делается запрос:
Все гости форума могут просматривать этот раздел. Только зарегистрированные пользователи могут создавать новые темы в этом разделе. Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.