2010-07-05 2 views
1

У меня есть пользовательская таблица (Пользователь) и 3 таблицы учебников (текст, видео и прочее).Как сгруппировать суммы столбцов из разных таблиц?

Каждый учебник имеет столбцы rating_positive и rating_negative и связан с пользователем (id).

Я хочу выбрать 10 пользователей с большинством учебников и суммой положительных/отрицательных отзывов о своих учебниках.

Я пробовал следующий запрос, но он не работает. Он возвращает слишком много результатов для tutorials_count/pos/neg. Как я могу сделать это правильно?

SELECT 
    u.id AS user_id, 
    (COUNT(t.id) + COUNT(v.id) + COUNT(o.id)) AS tutorials_count, 
    (SUM(t.rating_positive) + SUM(v.rating_positive) + SUM(o.rating_positive)) AS pos, 
    (SUM(t.rating_negative) + SUM(v.rating_negative) + SUM(o.rating_negative)) AS neg 
FROM 
    user u LEFT JOIN trick t ON u.id = t.submitter_id 
    LEFT JOIN video v ON u.id = v.submitter_id 
    LEFT JOIN other o ON u.id = o.submitter_id 
GROUP BY u.id 
ORDER BY tutorials_count DESC 
LIMIT 10 

ответ

2

Попробуйте сделать подзапрос с UNION ALL из трех таблиц вы заинтересованы, а затем присоединиться к тем, что:

SELECT 
    u.id AS user_id, 
    COUNT(submitter_id) AS tutorials_count, 
    IFNULL(SUM(rating_positive), 0) AS pos, 
    IFNULL(SUM(rating_negative), 0) AS neg 
FROM user u 
LEFT JOIN (
    SELECT submitter_id, rating_positive, rating_negative FROM trick 
    UNION ALL 
    SELECT submitter_id, rating_positive, rating_negative FROM video 
    UNION ALL 
    SELECT submitter_id, rating_positive, rating_negative FROM other 
) T1 
ON u.id = T1.submitter_id 
GROUP BY u.id 
ORDER BY tutorials_count DESC 
LIMIT 10 
+0

это работает. :-) Есть ли даже лучший запрос с более высокой производительностью? – Smock

1

ЛЕВАЯ JOIN и прекрасно, и будет работать лучше, чем unioning все три таблицы перед выполнением агрегации.

Проблема в том, что SUMmation на LEFT JOIN означает, что результат может быть NULL, который вы не можете добавить в сочетании с суммой из других столбцов. IE:

... SUM(t.rating_positive) + 1 

... вернет NULL, если SUM(t.rating_positive) нет записей, поддерживающих, так как NULL + 1 равен NULL.

Вы должны использовать COALESCE, чтобы преобразовать их к нулю математики для работы - IFNULL является приемлемой альтернативой, но это MySQL специфического поэтому не вероятно, чтобы быть портативными:

SELECT u.id AS user_id, 
      COALESCE(COUNT(t.id), 0) + COALESCE(COUNT(v.id), 0) + COALESCE(COUNT(o.id), 0) AS tutorials_count, 
      COALESCE(SUM(t.rating_positive), 0) + COALESCE(SUM(v.rating_positive), 0) + COALESCE(SUM(o.rating_positive), 0) AS pos, 
      COALESCE(SUM(t.rating_negative), 0) + COALESCE(SUM(v.rating_negative), 0) + COALESCE(SUM(o.rating_negative), 0) AS neg 
    FROM USER u 
LEFT JOIN trick t ON u.id = t.submitter_id 
LEFT JOIN video v ON u.id = v.submitter_id 
LEFT JOIN other o ON u.id = o.submitter_id 
GROUP BY u.id 
ORDER BY tutorials_count DESC 
    LIMIT 10 
+0

Это возвращает те же (неправильные) значения, что и без COALESCEs. – Smock

+0

. Эта и UNION-версия эквивалентны: UNION просто перестает работать с NULL для каждой таблицы, поэтому единственная возможность, с которой я могу прийти, заключается в том, что вы упростили запрос детали и не могут согласовать те с запросом, который я предоставил. –

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