0

I have the following SQL query.

SELECT    u.username, SUM(p.points) AS points,
          SUM(sp.spPoints) AS spPoints,
          (SUM(sp.spPoints) - SUM(p.points)) AS Puntos_Restantes
FROM      users as u
LEFT JOIN points as p ON (u.userid = p.userid)
LEFT JOIN sppoints AS sp ON (u.userid = sp.userid)
WHERE     u.userid = '1'
GROUP BY  u.userid

My goal is to SUM 2 fields and then subtract them but when I execute the above query, the second SUM is wrong.
The tables are like this:

points: pointId, userId, points    
sppoints: spPointId, userId, spPoints    

In points I have this amount: 25 and in spPoints: 10 but when I run the query I get :

points  spPoints    Puntos_Restantes
 25       30              5

What is going wrong here?

6
  • Can you show the table that you're pulling from? Or is it too large?
    – Dancrumb
    Commented Jul 5, 2012 at 13:58
  • Maybe you mean sum((sp.spPoints) - (p.points))
    – xQbert
    Commented Jul 5, 2012 at 13:58
  • Please show the full data needed to reproduce your example.
    – lanzz
    Commented Jul 5, 2012 at 13:58
  • This is the correct answer. You are subtracting sppoints - points.(SUM(sp.spPoints) - SUM(p.points)) 30 - 25 = 5. What are you saying is wrong? Commented Jul 5, 2012 at 14:00
  • the table "points" has 3 fields: pointId, userId, points i SUM all the points fields and the same with the table "sppoints" in points field the sum is 25 and in sppoints is 10 but the query shows different values in the spPoints and the result oviuly is diferent
    – user995691
    Commented Jul 5, 2012 at 14:02

1 Answer 1

2

The table users has a one-to-many relationship to both the other 2 tables. This causes the 2 joins to produce a mini-Carstesian product and multiple rows with same data in the points columns - which are then aggregated.

You can use subqueries to group by, and then join, to avoid this problem:

SELECT 
      u.username
    , COALESCE(p.pPoints,0) 
        AS pPoints
    , COALESCE(sp.spPoints,0) 
        AS spPoints
    , COALESCE(p.pPoints,0) - COALESCE(sp.spPoints,0)
        AS Puntos_Restantes
FROM 
      users as u
  LEFT JOIN 
      ( SELECT userid, SUM(points) AS pPoints
        FROM points
        WHERE userid = 1
        GROUP BY userid
      ) AS p
    ON u.userid = p.userid
  LEFT JOIN 
      ( SELECT userid, SUM(spPoints) AS spPoints
        FROM sppoints
        WHERE userid = 1
        GROUP BY userid
      ) AS sp
    ON u.userid = sp.userid
WHERE u.userid = 1 ;

If you want to have results for more than one user (or for all users), replace the three WHERE userid=1 conditions (or remove them altogether).

Indices on points(userid, points) and sppoints(userid, spPoints) will help with efficiency.

3
  • nice solution. But, why you include WHERE userid = 1 in subqueries? Is for performance reasons? To avoid all table aggregations? Commented Jul 5, 2012 at 14:07
  • @danihp: For performance. So, the subqueries calculate the points for the specific user only. Commented Jul 5, 2012 at 14:09
  • Ok you are the man! thanks! But this "COALESCE(sp.spPoints,0) - COALESCE(p.pPoints,0)" should be like this: "COALESCE(p.pPoints,0) - COALESCE(sp.spPoints,0)" cause the result isnt negative.
    – user995691
    Commented Jul 5, 2012 at 14:16

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.