Как оптимизировать запрос MySQL, чтобы выбранное значение в предложении WHERE вычислялось только один раз?

#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 ?