2016-12-05 9 views
0

Скажем, у меня есть две таблицы. businesses и reviews для предприятий.Mysql Bayesian и сортировать по звездам

businesses стол:

+----+-------+ 
| id | title | 
+----+-------+ 

reviews стол:

+----+-------------+---------+------+ 
| id | business_id | message | rate | 
+----+-------------+---------+------+ 

каждый отзыв имеет rate (от 1 до 5 звезд)
Я хочу, чтобы отсортировать предприятия по их обзоры ставок, основанный на Bayesian Ranking с условием наличия не менее 2 отзывов.

Вот мой запрос:

SELECT b.id, 
(SELECT COUNT(r.rate) as rr FROM reviews r WHERE r.business_id = b.id) as rr, 
(SELECT 
     ((COUNT(r.rate)/(COUNT(r.rate) + 2)) AVG(r.rate) + 
     (2 /(COUNT(r.rate) + 2)) 4) 
    FROM reviews r where r.business_id = b.id AND rr > 2 
) as score 
FROM businesses b 
order by score desc 
LIMIT 4 

это выведет меня:

+------+----+------------+ 
| id | rr | score  | 
+------+----+------------+ 
| 992 | 14 | 4.31250000 | 
+------+----+------------+ 
| 237 | 3 | 4.2000000 | 
+------+----+------------+ 
| 19 | 5 | 4.0000000 | 
+------+----+------------+ 
| 1009 | 12 | 3.9285142 | 
+------+----+------------+ 

У меня есть два вопроса:

  1. как вы видите в ((COUNT(r.rate)/(COUNT(r.rate) + 2)) AVG(r.rate) + (2 /(COUNT(r.rate) + 2)) 4) FROM reviews r where r.business_id = b.id AND rr > 2) некоторые функции работают более чем один раз, например COUNT или AVG. они работают один раз в фоновом режиме и, возможно, кэшируют результ? ИЛИ запускать для каждого звонка?

  2. Есть ли эквивалентный запрос для этого, но более оптимизирован?

благодарит заранее.

+0

Вы даже получите «правильный» ответ? Я думаю, что 'rr' не должно быть видимым для второго подзапроса. –

ответ

1

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

Однако вы можете изменить запрос, чтобы присоединиться к второму запросу. Таким образом, вы не выполняете 2 подзапроса для каждой строки.

SELECT b.id, 
     sub0.rr, 
     sub0.score 
FROM businesses b 
INNER JOIN 
(
    SELECT r.business_id, 
      COUNT(r.rate) AS rr , 
      ((COUNT(r.rate)/(COUNT(r.rate) + 2)) AVG(r.rate) + (2 /(COUNT(r.rate) + 2)) 4) AS score 
    FROM reviews r 
    GROUP BY r.business_id 
    HAVING rr > 2 
) sub0 
ON sub0.business_id = b.id 
ORDER BY score DESC 
LIMIT 4 

Обратите внимание, что результат здесь очень немного отличается, поскольку это исключает записи с только 2 обзоров, в то время как ваш запрос будет возвращать их, но со счетом NULL. Я ушел в очевидные недостающие операторы (т.е. до AVG (r.rate) и до 4) AS оценка из вашего исходного запроса.

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

SELECT b.id, 
     sub0.rr, 
     ((rr/(rr + 2)) arr + (2 /(rr + 2)) 4) AS score 
FROM businesses b 
INNER JOIN 
(
    SELECT r.business_id, 
      COUNT(r.rate) AS rr , 
      AVG(r.rate) AS arr 
    FROM reviews r 
    GROUP BY r.business_id 
    HAVING rr > 2 
) sub0 
ON sub0.business_id = b.id 
ORDER BY score DESC 
LIMIT 4 
+0

Благодарим вас за ответ. Я попытался запустить ваш второй запрос, но ошибки 'b.id неизвестны' в строке 12. во внутреннем выборе, где. Поэтому я изменил это. https://codetidy.com/9750/, но суб-запрос получает все rr> 2 – Pars

+1

@Pars - Упс, исправлено. Суб-запрос будет получать все, где счетчик> 2, но он соединен с бизнес-таблицей, а затем вычисляется счетчик для вычисления оценки. Таким образом, соединение с суб-запросом исключает любые из двух или менее обзоров, в то время как ORDER/LIMIT в основном запросе ограничивает это до 4 возвращенных строк. – Kickstart