Как эффективно (независимо от БД) выбирать случайные записи из таблицы? - PullRequest
2 голосов
/ 01 сентября 2011

Это похоже на невероятно простую проблему, однако она не работает так тривиально, как я ожидал.

У меня есть клуб, в котором есть члены клуба, и я бы хотел вывести двух случайных членов из клуба.

Использование RANDOM ()

Один из способов - использовать случайный порядок:

club.members.find(:all, :order => 'RANDOM()').limit(2)

Однако это отличается для SqLite (база данных dev) и Postgres (production), поскольку в MySql команда имеет вид RAND().

Хотя я мог бы начать писать обертки вокруг этого, я чувствую, что тот факт, что это еще не было сделано и, кажется, не является частью ActiveRecord, говорит мне кое-что, и что СЛУЧАЙНЫЙ, возможно, не правильный путь.

Извлечение предметов напрямую по их индексу

Другой способ сделать это - вывести набор по порядку, но затем выбрать случайные записи из него:

Прежде всего нам нужно сгенерировать последовательность из двух уникальных индексов, соответствующих членам:

all_indices = 1..club.members.count
two_rand_indices = all_indices.to_a.shuffle.slice(0,2)

Это дает массив с двумя индексами, которые гарантированно будут уникальными и случайными. Мы можем использовать эти индексы, чтобы вытащить наши записи

@user1, @user2 = Club.members.values_at(*two_rand_indices)

Какой метод лучше?

Хотя второй метод выглядит довольно неплохо, я также чувствую, что могу что-то упустить и может усложнить простую проблему. Я, очевидно, не первый, кто занялся этим, так какой же самый лучший и наиболее эффективный для SQL маршрут через него?

Ответы [ 3 ]

1 голос
/ 08 сентября 2011

Проблема с вашим первым методом состоит в том, что он сортирует всю таблицу по нечитаемому выражению, просто чтобы взять две строки.Это плохо масштабируется.

Проблема с вашим вторым методом аналогична: если в вашей таблице 10 9 строк, вы создадите большой массив из to_a.Это займет много памяти и времени, чтобы перетасовать его.

Кроме того, используя values_at, разве вы не предполагаете, что для каждого значения первичного ключа есть строка от 1 до счетчика без пробелов?Вы не должны предполагать, что.

Вместо этого я бы порекомендовал:

  1. Подсчет строк в таблице.

    c = Club.members.count
    
  2. Выберите два случайных числа между 1 и счетчиком.

    r_a = 2.times.map{ 1+Random.rand(c) }
    
  3. Запросите к таблице таблицу с пределом и смещением .
    Не используйте ORDER BY, просто полагайтесь на произвольный порядок СУБД.

    for r in r_a
        row = Club.members.limit(1).offset(r)
    end
    

См. Также:

0 голосов
/ 01 сентября 2011

попробуйте использовать гем randumb , он реализует второй метод, который вы упомянули

0 голосов
/ 01 сентября 2011

Функция Order By RAND () в MySQL:

ORDER BY RAND() LIMIT 4

Выбирает случайные 4 строки, когда приведенное выше предложение является последним в запросе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...