2015-10-12 4 views
0

Я использую следующий запрос, чтобы получить случайную строку в MySql. И, я думаю, это будет намного быстрее, чем ORDER BY RAND(), поскольку он просто возвращает строку после случайного подсчета строк и не требует упорядочения строк.Оптимизация моего пользовательского запроса RAND()

SELECT COUNT(ID) FROM TABLE_NAME 

!-- GENERATE A RANDOM NUMBER BETWEEN 0 and COUNT(ID)-1 --! 

SELECT x FROM TABLE_NAME LIMIT RANDOM_NUMBER,1 

Но, мне нужно знать, можно ли каким-либо образом оптимизировать его и есть более быстрый метод.

Я также был бы признателен, если я могу объединить 2 запросов в LIMIT не поддерживает такие подзапросов (как я знаю).

EDIT- Способ, которым работает мой запрос, не случайным образом генерирует какой-либо ID. Но вместо этого он генерирует случайное число. между 0 и итогом нет. строк. И тогда я использую это. как смещение, чтобы получить строку рядом с этим случайным числом.

+0

Ваша техника будет в лучшем случае средней в два раза быстрее, чем обычная 'ORDER BY RAND() LIMIT1'. Это связано с тем, что нет возможности перепрыгнуть N строк в таблицу. –

ответ

1

Я думаю, что запрос вы хотите:

select x.* 
from tablename x 
where x.id >= random_number 
order by x.id 
limit 1; 

Это должно использовать индекс x.id и должен быть довольно быстро. Вы можете комбинировать их как:

select x.* 
from tablename x cross join 
    (select cast(max(id) * rand() as int) as random_number from tablename 
    ) c 
where x.id >= random_number 
order by x.id 
limit 1; 

Обратите внимание, что вы должны использовать max(id) вместо count(), потому что могут быть пробелы в ид. В подзапросе также должен использоваться индекс на id.

EDIT:

Я не буду оборонительный о вышеуказанном решении. Он возвращает случайный идентификатор, но идентификатор не равномерно распределен.

Мой предпочтительный метод, в любом случае, это:

select x.* 
from tablename x cross join 
    (select count(*) as cnt from x) cnt 
where rand() < 100/cnt 
order by rand() 
limit 1; 

Это очень, очень маловероятно, что вы не получите ни одной строки с условием where (это возможно, но очень маловероятно). Финал order by rand() обрабатывает только 100 строк, поэтому он должен идти довольно быстро.

+1

Это хороший ответ, но следует отметить, что он не будет работать должным образом, если идентификаторы не являются последовательными (например, у вас есть пробелы в поле id) – itoctopus

+0

Я принял ответ, но, возможно, вам следует прочитать правки I сделал. – prakhar19

+0

@itoctopus. , , Это specfically работает, когда есть пробелы. Возможно, вы имеете в виду исходный вопрос, который использовал 'count()', а не 'max()'. –

-1

Попробуйте кэшировать результат первого запроса и использовать его во втором запросе. Использование обоих в одном запросе будет очень тяжелым в системе.

Что касается второго вопроса, попробуйте следующее:

SELECT x FROM TABLE_NAME WHERE ID = RANDOM_NUMBER 

выше запрос намного быстрее, чем ваш (при условии, ID индексируется)

Конечно, приведенный выше запрос предполагает, что вы используете последовательные идентификаторы (без пробелов). Если есть пробелы, тогда вам нужно будет создать другое последовательное поле (возможно, вызывать его ID2), а затем выполнить вышеуказанный запрос в этом поле.

+0

Конечно, есть пробелы, иначе зачем бы я использовал 2 запроса. :-) – prakhar19

1

EDIT: Мой ответ предполагает MySql < 5.5.6 где вы не можете передать переменную в LIMIT и OFFSET. В противном случае метод OP является лучшим.

Самое надежное решение, imo, было бы ранжировать ваши результаты, чтобы устранить пробелы. Мое решение может быть не оптимальным, так как я не привык к MySQL, но логика работает (или работает в моем SQLFiddle).

SET @total = 0; 

SELECT @total := COUNT(1) FROM test; 

SET @random=FLOOR(RAND()*@total)+1; 
SET @rank=0; 

SELECT * from 
    (SELECT @rank:[email protected]+1 as rank, id, name 
    FROM test 
    order by id) derived_table 
where rank = @random; 

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

В принципе, вы генерировать случайные числа строк с (это один из места, где есть наиболее вероятно, оптимизации должны быть сделаны):

SET @total = 0; 
SELECT @total := COUNT(1) FROM test; 
SET @random=FLOOR(RAND()*@total)+1; 

Тогда вы ранжировать все ваши строки для устранения пробелов:

SELECT @rank:[email protected]+1 as rank, id, name 
FROM test 
order by id 

И вы выбираете случайно выбранную строку:

SELECT * from 
    (ranked derived table) derived_table 
where rank = @random; 
+0

Нет необходимости удалять пробелы или использовать ранги, если вы просто получите следующую строку (не следующую PRIMARY_KEY) случайному счету или id. – prakhar19

+0

Нет необходимости удалять пробелы, но выбор чего-то случайного из непрерывного списка намного проще, чем эклектичный. В принципе, вам нужно выбирать между учетными записями на каждом шаге вашего запроса или исправлением этих отверстий. –

+0

Фактически он основан на том, что LIMIT автоматически возвращает строку рядом со смещением, неважно, является ли это следующим PRIMARY_KEY или нет. И, что я хотел, это запрос, который будет работать быстро во всех случаях. – prakhar19

0

Там 5 методов в http://mysql.rjweb.org/doc.php/random. Никто из них не должен смотреть на всю таблицу.

У вас есть AUTO_INCREMENT? С или без пробелов? И другие вопросы требуют ответа, чтобы знать, какой метод в этой ссылке даже применим.

Смежные вопросы