2012-02-20 2 views
1

Вчера я тестировал один запрос в течение нескольких часов и часов, но мне так и не удалось. Эти три таблицы:SQL Присоединиться к трем таблицам

USERS: 
#### id: 1 ##### name: Admin ##### Hometown: The Hague 

POSTS: 
#### id: 1 ##### userid: 1 ##### title: Test I ##### opinion: agree 
#### id: 2 ##### userid: 1 ##### title: Nope.. ##### opinion: disagree 

REACTIONS: 
#### id: 1 ##### userid: 1 ##### opinion: agree 
#### id: 2 ##### userid: 1 ##### opinion: disagree 

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

Это запрос, я использую сейчас:

SELECT 
     u.name, u.hometown, 

     SUM(IF(r.opinion="disagree",1,0)) AS agrees 
     SUM(IF(r.opinion="disagree",1,0)) AS disagrees, 

     SUM(IF(p.opinion="agree",1,0)) AS compliments, 
     SUM(IF(p.opinion="disagree",1,0)) AS complaints 

    FROM 
     users AS u 

    LEFT JOIN 
     reactions AS r 
    ON 
     r.userid = u.id 

    LEFT JOIN 
     posts AS p 
    ON 
     p.userid = u.id 

    WHERE 
     u.id = 1 

Проблема заключается в том, что это не дает мне правильную информацию. Он возвращает значения, как 8 положительных реакций, хотя в БД есть только две реакции.

Я думаю, что это имеет какое-то отношение к GROUP BY p.id, r.id, но я пробовал это, и это не сработало ... Может ли кто-нибудь просветить меня?

Заранее благодарен!

+1

Что RDBMS? Вам нужно использовать подзапросы, чтобы суммировать реакции пользователя отдельно с сообщениями пользователя, потому что в настоящее время вы перекрестно присоединяетесь к сообщениям и реакциям, что будет означать, что вы получите много дублирования. – GarethD

+0

Кажется, что вы не получили запятую после согласования. – xQbert

+0

Да, я просто набрал это в своем браузере, а не тот код, который я использовал. – Diederik

ответ

0

В зависимости от СУБД, вы могли бы быть в состоянии сделать это:

SELECT 
    *, 
    (SELECT COUNT(*) FROM POSTS WHERE POSTS.userid = USERS.id and opinion = 'agree') compliments, 
    (SELECT COUNT(*) FROM POSTS WHERE POSTS.userid = USERS.id and opinion = 'disagree') complaints, 
    (SELECT COUNT(*) FROM REACTIONS WHERE REACTIONS.userid = USERS.id and opinion = 'agree') positive_reactions, 
    (SELECT COUNT(*) FROM REACTIONS WHERE REACTIONS.userid = USERS.id and opinion = 'disagree') negative_reactions 
FROM USERS 
+1

Это работает, но 4 взаимосвязанных подзапроса будут * обычно * лучше реорганизовываться на соединение по некоррелированным подзапросам. – MatBailie

+0

@Dems Не на СУБД с приличным оптимизатором. Коррелированные подзапросы не обязательно означают NESTED LOOP, и, что интересно, NESTED LOOP не всегда является самым медленным способом. Создание такого рода заявлений без фактического ** измерения ** не оправдано. –

+0

Это действительно сработало для меня. Решение Dems дало мне ошибку, я постараюсь сделать это исправленным. – Diederik

2

Это не будет группировка, ваш присоединяется присоединяются несколько записей образуют одну таблицу, в одной записи в другую. Это вызывает дублирование.

Например, для одной записи в таблице пользователя вы можете иметь 3 ответа в ответах и ​​3 ответа в сообщениях. Ваш запрос возвращает 9 записей, потому что все реакции этого пользователя будут соединены все посты этого пользователя ...

userid | reaction_id | post_id 
    1   1   1 
    1   1   2 
    1   1   3 
    1   2   1 
    1   2   2 
    1   2   3 
    1   3   1 
    1   3   2 
    1   3   3 

Это означает, что вам нужно агрегировать реакции отдельно от должностей ...

SELECT 
    u.name, u.hometown, 

    r.agrees 
    r.disagrees, 

    p.compliments, 
    p.complaints 

FROM 
    users AS u 
LEFT JOIN 
(
    SELECT 
    userid, 
    SUM(IF(r.opinion="agree",1,0)) AS agrees 
    SUM(IF(r.opinion="disagree",1,0)) AS disagrees, 
    FROM 
    reactions 
    GROUP BY 
    userrid 
) 
    AS r 
    ON r.userid = u.id 
LEFT JOIN 
(
    SELECT 
    userid, 
    SUM(IF(p.opinion="agree",1,0)) AS compliments, 
    SUM(IF(p.opinion="disagree",1,0)) AS complaints 
    FROM 
    posts 
    GROUP BY 
    userid 
) 
    AS p 
    ON p.userid = u.id 
WHERE 
    u.id = 1 
+0

Были некоторые ошибки синтаксиса, но после того, как я исправил их, он все еще не дает мне результат, который мне нужен: номера отключены. Решение Бранко действительно работает, поэтому я думаю, что пойду с этим. Спасибо за вашу помощь, хотя :)! – Diederik

0

Как насчет?

select id, name, hometown, sum(agrees) agrees, sum(disagrees) disagrees, 
    sum(compliments) compliments, sum(complaints) complaints 
from (
    select u.id, u.name, u.hometown, 
    if(p.opinion = 'agree', 1, 0) agrees, 
    if(p.opinion = 'disagree', 1, 0) disagrees, 
    0 compliments, 0 complaints 
    from users u 
    left join posts p on u.id = p.userid 
    union all 
    select u.id, u.name, u.hometown, 
    0, 0, 
    if(r.opinion = 'agree', 1, 0), 
    if(r.opinion = 'disagree', 1, 0) 
    from users u 
    left join reactions r on u.id = r.userid 
) as S 
group by id, name, hometown 
0
select users.name, users.hometown, myPosts.compliments, myPosts.complaints,  myReaction.agrees, myReaction.disagrees 
from Users users 
LEFT JOIN 
(
    select post.userid as userid 
     , SUM(CASE WHEN post.opinion = 'agree' THEN 1 END) as compliments 
     , SUM(CASE WHEN post.opinion = 'disagree' THEN 1 END) as complaints 
    from Posts post 
    group by post.userid 
) as myPosts 
on users.id = myPosts.userid 
LEFT JOIN 
(
    select reaction.userid as userid 
    , SUM(CASE WHEN reaction.opinion = 'agree' THEN 1 END) as agrees 
    , SUM(CASE WHEN reaction.opinion = 'disagree' THEN 1 END) as disagrees 
    from Reaction reaction 
    group by reaction.userid 
) as myReaction 
on users.id = myReaction.userid 
Смежные вопросы