#mysql
#mysql
Вопрос:
Мне нужно случайным образом выбрать эффективным способом 10 строк из моей таблицы.
Я обнаружил, что следующее работает хорошо (после запроса я просто выбираю 10 случайных элементов в PHP из 10-30, которые я получаю из запроса):
SELECT * FROM product WHERE RAND() <= (SELECT 20 / COUNT(*) FROM product)
Однако подзапрос, хотя и относительно дешевый, вычисляется для каждой строки в таблице. Как я могу это предотвратить? С переменной? Объединение?
Спасибо!
Комментарии:
1. почему бы и нет? «выберите * из YOUR_TABLE order с помощью rand() limit 10»
2. @Cristian-Boariu Это вообще НЕЭФФЕКТИВНО для больших таблиц (их нужно полностью отсортировать)
Ответ №1:
Это могла бы сделать переменная. Что-то вроде этого:
SELECT @myvar := (SELECT 20 / COUNT(*) FROM product);
SELECT * FROM product WHERE RAND() <= @myvar;
Или из документа MySQL math functions:
Вы не можете использовать столбец со значениями RAND() в предложении ORDER BY, потому что ORDER BY будет оценивать столбец несколько раз. Однако вы можете извлекать строки в случайном порядке следующим образом:
mysql> SELECT * FROM tbl_name ORDER BY
> RAND();
ПОРЯДОК ПО RAND() в сочетании с LIMIT равен
полезно для выбора случайной выборки
из набора строк:
mysql> SELECT * FROM table1, table2
> WHERE a=b AND c<d -> ORDER BY RAND()
> LIMIT 1000;
RAND() не предназначен для идеального
генератор случайных чисел. Это быстрый способ
генерируйте случайные числа по требованию, которые
переносим между платформами для
та же версия MySQL.
Комментарии:
1. Я не могу использовать
ORDER BY RAND()
, потому что это слишком неэффективно для большой таблицы. Мне нравится ваше первое предложение, но могу ли я сделать это всего за один запрос?2. Да, вы должны быть в состоянии выполнить этот первый фрагмент кода (используя переменную) как единый оператор без проблем. Только не забудьте разделитель операторов; (точка с запятой).
Ответ №2:
Это очень специфичный для mysql трюк, но, поместив его в другой подзапрос, MySQL сделает его постоянной таблицей и вычислит его только один раз.
ВЫБЕРИТЕ * ИЗ продукта, ГДЕ RAND() <= ( выберите * из ( ВЫБЕРИТЕ 20 / COUNT(*) ИЗ product ) в качестве const_table )
Комментарии:
1. В MySQL 5.0 это не работает :
#1248 - Every derived table must have its own alias
2. (Хотя мне нравится тот факт, что это просто — хотя и уродливо 😉 — и один запрос)
3. Ошибка 1248 — это опечатка с моей стороны — просто неправильное размещение круглых скобок. Вы можете повторить попытку?
4. Это работает. Извините, я пропустил опечатку в скобках.
COUNT(*)
Все еще довольно долго (50 мс для подсчета моих 10000 строк? Давай!), Но этого будет достаточно. Спасибо!
Ответ №3:
SELECT * FROM product ORDER BY RAND() LIMIT 10
Комментарии:
1. Это недостаточно эффективно для больших таблиц
Ответ №4:
Не используйте порядок по rand(). Это приведет к сканированию таблицы. Если у вас в таблице вообще много данных, это будет неэффективно вообще. Сначала определите, сколько строк в таблице:
select count(*) from table
может сработать для вас, хотя вам, вероятно, следует кэшировать это значение в течение некоторого времени, поскольку оно может быть медленным для больших наборов данных.
explain select * from table
предоставит вам статистику БД для таблицы (сколько строк, по мнению статистики, в таблице) Это намного быстрее, однако это менее точно и еще менее точно для InnoDB.
как только у вас будет количество строк, вы должны написать некоторый код типа:
псевдокод:
String SQL = "SELECT * FROM product WHERE id IN (";
for (int i=0;i<numResults;i ) {
SQL = (int)(Math.rand() * tableRows) ", ";
}
// trim off last ","
SQL.trim(",");
SQL = ")";
это обеспечит вам быстрый поиск в PK и позволит избежать сканирования таблицы.
Комментарии:
1. Я знаю, что не могу использовать
ORDER BY RAND()
. Я также не могу использовать ваше решение, потому что мои идентификаторы не являются непрерывными (они даже не числовые …)2. Числовой PK будет работать лучше. Но даже если у вас тип CHAR PK, вы все равно можете использовать этот метод, просто не используйте int . Нет требования, чтобы они были смежными. Рассмотрим:
SELECT * FROM product WHERE id IN ('abc123', 'pkr982', 'zfw012')
. У вас могут возникнуть проблемы с созданием идентификаторов, если они не являются числовыми. Рассмотрите возможность добавления целого числа auto_increment PK ?