Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Общие вопросы > Выборка нескольких случайных значений из MySQL


Автор: console 18.9.2007, 13:56
Есть таблица... допустим там 200 записей. Мне нужно выбрать 5 случайных... Как это осуществить?

Автор: BobiKK 18.9.2007, 14:03
Код

SELECT * FROM `table` ORDER BY RAND() LIMIT 5

Быстро, но оптимизации никакой. Для таблицы в 200 записей, однако, не критично

Автор: console 18.9.2007, 14:06
Спасибо, 200 не предел... будет много записей, как оптимизировать?

Автор: BobiKK 18.9.2007, 14:17
Оптимизировать тяжко. Можете почитать дискуссию здесь:
http://www.mysqlperformanceblog.com/2006/09/01/order-by-limit-performance-optimization/
Варианты там тоже имеются

Автор: console 18.9.2007, 15:21
Составление оптимальных запросов целое искусство...

Автор: console 19.9.2007, 11:03
http://jan.kneschke.de/projects/mysql/order-by-rand/ приведена хорошая статья по выборке случайных значений. Примеры идут для выбора одного случайного. В конце статьи автор предлагает пример функции для выборки произвольного количества.
Вопрос: не могут ли совпадать значения?
В MySQL reference пишется, что RAND() генерит числа от 0 до 1. В примере подсчитал, что кол-во знаков после запятой = 14. Насколько безопасно использование такого кода?

Код

DELIMITER $$
DROP PROCEDURE IF EXISTS get_rands$$
CREATE PROCEDURE get_rands(IN cnt INT)
BEGIN
  DROP TEMPORARY TABLE IF EXISTS rands;
  CREATE TEMPORARY TABLE rands ( rand_id INT );

loop_me: LOOP
    IF cnt < 1 THEN
      LEAVE loop_me;
    END IF;

    INSERT INTO rands
       SELECT r1.id
         FROM random AS r1 JOIN
              (SELECT (RAND() *
                            (SELECT MAX(id)
                               FROM random)) AS id)
               AS r2
        WHERE r1.id >= r2.id
        ORDER BY r1.id ASC
        LIMIT 1;

    SET cnt = cnt - 1;
  END LOOP loop_me;
END$$
DELIMITER ;

CALL get_rands(4);
SELECT * FROM rands;
+---------+
| rand_id |
+---------+
|  133716 |
|  702643 |
|  112066 |
|  452400 |
+---------+

Автор: sTa1kEr 19.9.2007, 15:11
Цитата(console @  19.9.2007,  11:03 Найти цитируемый пост)
Вопрос: не могут ли совпадать значения?

Еще как могут, там никто и не обещает, что рандомные значения будут уникальные. Для гарантированной уникальности я бы немного модифицировал хранимку
Код

DELIMITER $$
DROP PROCEDURE IF EXISTS get_rands$$
CREATE PROCEDURE get_rands(IN cnt INT)
BEGIN
  DROP TEMPORARY TABLE IF EXISTS rands;
  CREATE TEMPORARY TABLE `rands` (`rand_id` INTEGER,  PRIMARY KEY (`rand_id`));
  IF (SELECT COUNT(*) FROM random) >= cnt THEN
loop_me: LOOP

    IF (SELECT COUNT(*) FROM rands) >= cnt THEN
      SELECT * FROM rands;
      LEAVE loop_me;
    END IF;

    INSERT IGNORE INTO rands
       SELECT r1.id
         FROM random AS r1 JOIN
              (SELECT (RAND() *
                            (SELECT MAX(id)
                               FROM random)) AS id)
               AS r2
        WHERE r1.id >= r2.id
        ORDER BY r1.id ASC
        LIMIT 1;
    -- SET cnt = cnt - 1;
  END LOOP loop_me;
  END IF;
END$$
DELIMITER ;

Дополнительная проверка нужна, что бы не было зацикливания, если параметр cnt больше общего количества строк.

Автор: ewolf 19.9.2007, 15:49
Вот что-то сомнительно мне, что данная хранимая процедура будет производительней одного SQL запроса

Автор: sTa1kEr 19.9.2007, 16:25
Цитата(ewolf @  19.9.2007,  15:49 Найти цитируемый пост)
Вот что-то сомнительно мне, что данная хранимая процедура будет производительней одного SQL запроса 

Зависит от структуры таблицы, количества записей в ней и количества необходимых рандомных значений, т.к. ORDER BY RAND() пересортировывает всю таблицу используя filesort. Для 100-200 записей, вероятно, эта хранимка действительно будет дольше работать, а вот для 1000000 ...

Если очень хочется одним запросом, то можно ускорить этот запрос следующим образом
Код

SELECT t.* 
FROM `table` t
INNER JOIN (SELECT `id` FROM `table` ORDER BY RAND() LIMIT 5) AS rnd ON t.`id` = rnd.`id`

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