I have a large but somewhat straightforward SQL query. Basically, users on my site develop reputations for different types of activities, such as writing reviews, leaving comments, and adding entries to our database. For the most part, these points are stored in the reputable_actions table, and I retrieve them by LEFT JOINing the reputable_actions table repeatedly. This feels sloppy, but it mostly work.
The problem I’m experiencing is with two of the reputations, “reviewer” and “community.” Unlike the others, they aren’t stored in the reputable_actions table. Instead, their values are derived from the votes table, which I access by first LEFT JOINing the comments table. For some reason, joining the comments table causes all my other reputations to increase exponentially. In one trial, the “archivist” reputation was suppose to be 25, but when I joined the comments, it ballooned to 10050.
I’m a novice with SQL and I’ve tried what I know (namely, applying GROUP BY clauses to users.id), but I haven’ had any luck yet. Some guidance would be greatly appreciated.
SELECT users.*,
SUM(COALESCE(reviewers.value, 0)) as reviewer,
SUM(COALESCE(communities.value,0)) as community,
SUM(COALESCE(developers.value,0)) as developer,
SUM(COALESCE(moderators.value,0)) as moderator,
SUM(COALESCE(marketers.value,0)) as marketer,
SUM(COALESCE(archivists.value,0)) as archivist,
SUM(COALESCE(karmas.value,0)) as karma
FROM `users`
LEFT JOIN comments AS impressions
ON impressions.user_id = users.id
AND impressions.type = 'impression'
LEFT JOIN comments AS replies
ON replies.user_id = users.id
AND replies.type = 'reply'
LEFT JOIN votes AS reviewers
ON reviewers.voteable_type = 'impression'
AND reviewers.voteable_id = impressions.id
LEFT JOIN votes AS communities
ON communities.voteable_type = 'reply'
AND communities.voteable_id = replies.id
LEFT JOIN reputable_actions AS developers
ON developers.reputation_type = 'developer'
AND developers.user_id = users.id
LEFT JOIN reputable_actions AS moderators
ON moderators.reputation_type = 'moderator'
AND moderators.user_id = users.id
LEFT JOIN reputable_actions AS marketers
ON marketers.reputation_type = 'marketer'
AND marketers.user_id = users.id
LEFT JOIN reputable_actions AS archivists
ON archivists.reputation_type = 'archivist'
AND archivists.user_id = users.id
LEFT JOIN reputable_actions AS karmas
ON karmas.reputation_type = 'karma'
AND karmas.user_id = users.id
GROUP BY users.id
Basically you need to do two separate group bys, and combine the results. There’s a trick to avoid joining multiple times, it may not by faster if you have many other voteable types, or reputation types.
Example (with no data). If your tables are structured differently to this, let me know.