2010-01-11 3 views
7

Я запускаю запрос MySQL для ранжирования пользователей моего сайта в соответствии с количеством обзоров книг и обзоров рецептов, которые они внесли. После начальных проблем с несколькими JOIN-запросами я переключился на ряд подзапросов, что намного, намного быстрее. Однако, хотя я могу извлечь количество отзывов от каждого участника, я не могу понять, как их добавить, чтобы сортировать по общему числу.Как добавить результаты нескольких подзапросов?

Вот текущий запрос:

SELECT users.*, 
    (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
    (SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) as bookreviews, 
    (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID) as recipereviews 
FROM users 

Мне нужно добавить вместе bookreviews и recipereviews, чтобы получить 'reviewtotals'. MySQL не позволит вам использовать простой синтаксис для выполнения вычислений на псевдонимах, но я предполагаю, что есть другой способ сделать это?

+0

@mandel, мне любопытно, о ваших проблемах с 'JOIN' обычно, я решить такого рода проблемы с' JOIN' - см мой ответ на Справка. По моему опыту, который обычно намного быстрее для MySQL, чем несколько взаимосвязанных подзапросов, подобных вам. Мне было бы интересно услышать, будет ли в вашем случае решение подзапроса быстрее, чем JOIN. Я был бы очень признателен, если бы вы могли сообщить мне. –

+0

Мои проблемы с JOIN исходили из неопытности, а не из философии. Мой предыдущий JOIN привел к ужасно медленному запросу - 9 секунд; оказалось, что он создает декартово произведение. Я спросил об этом здесь: http://stackoverflow.com/questions/2030032/is-performing-a-count-calculation-slowing-down-my-mysql-query. Когда я обратился к выполнению подзапросов, скорость резко улучшилась, и я мог понять, что я делаю! – mandel

+0

См. Мой ответ на ваш пост для сравнения скорости. Ваш запрос JOIN немного быстрее, но только 4/100ths секунды при текущем размере DB. – mandel

ответ

9

Заверните его в подзапрос:

SELECT *, 
     bookreviews + recipereviews AS totalreviews 
FROM (
     SELECT users.*, 
       (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
       (SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) as bookreviews, 
       (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID) as recipereviews 
     FROM users 
     ) q 
0

Вы пробовали следующее?

SELECT users.*, 
    (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
    ((SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) + 
    (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID)) as reviewtotal 
FROM users 
+0

Спасибо - это почти все, но мне все же нужно генерировать итоговые значения из bookreviews и recipereviews, а также рассчитанную сумму. – mandel

1

Два варианта:

Вариант 1: Гадкий ад, и, вероятно, медленно (зависит от кэша запросов):

SELECT users.*, 
    (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
    (SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) as bookreviews, 
    (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID) as recipereviews 
    (SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) + (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID) as reviewtotals 
FROM users 

Вариант 2: Сохранить результаты во временную таблицу и затем запросить эту таблицу

Возможно, это будет работать (не пробовал)

SELECT *, bookreviews+recipereviews as reviewtotals FROM 
(SELECT users.*, 
    (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
    (SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) as bookreviews, 
    (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID) as recipereviews 
FROM users) u 
+0

Номер два почти работает, но вы должны добавить псевдоним к первому подзапросу - тогда он идентичен решению @ Quassnoi, хотя это было в минуту раньше. Благодаря! – mandel

+0

Я видел, что был еще один ответ, набрав ... –

1

Если вы хотите быть безопасно и быстро, сделать это следующим образом:

SELECT users.* 
,  titles.num       titles 
,  book_reviews.num      book_reviews 
,  recipe_reviews.num     recipe_reviews 
,  book_reviews.num + recipe_reviews.num total_reviews 
FROM  users 
LEFT JOIN (
      SELECT user_ID, count(*) AS num 
      FROM  bookshelf 
      GROUP BY user_ID 
     ) as titles 
ON  users.ID = titles.user_ID 
LEFT JOIN (
      SELECT user_ID, count(*) AS num 
      FROM  book_reviews 
      GROUP BY user_ID 
     ) as book_reviews 
ON  users.ID = reviews.user_ID 
LEFT JOIN (
      SELECT user_ID, count(*) AS num 
      FROM  recipe_reviews 
      GROUP BY user_ID 
     ) as recipe_reviews 
ON  users.ID = recipes.user_ID 

Если вы хотите придерживаться подзапросов в SELECT списке, и хотите, чтобы это было безопасно, взгляните на решение Кваснуи.

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

SELECT users.*, 
     (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles, 
     @bookreviews:=(
      SELECT count(*) 
      FROM book_reviews 
      WHERE book_reviews.user_id = users.ID 
     ) as bookreviews, 
     @recipereviews:=(
      SELECT count(*) 
      FROM recipe_reviews 
      WHERE recipe_reviews.user_id = users.ID 
     ) as recipereviews, 
     @bookreviews + @recipereviews as total_reviews 
FROM users 
+0

Интересно: Вариант 1 немного быстрее, чем решение Кваснуи (0.02 сек против 0.024 с), тогда как Вариант 2 составляет 0,88 сек, поэтому сравнительно немного медленнее. Я считаю, что Quassnoi намного проще в плане удобочитаемости (и моего понимания того, что происходит). Есть ли преимущества в использовании метода JOIN в первом примере? – mandel

+0

mandel: да, преимущества становятся очевидными, когда комплекты, с которыми вы имеете дело, растут в размерах. JOIN намного лучше масштабируется. Однако в эти часы я не думаю, что мы можем сказать что-то убедительное. Я бы проигнорировал такую ​​небольшую разницу. Но все же вы должны увидеть значительные различия, когда получите больше данных для решения. Решение подзапроса выполняет каждый подзапрос для каждой строки внешнего запроса. Соединение вложенного цикла обычно намного более эффективно. –

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