0

I have the following tables:

create table Users (
  Id int
)

create table Vals1 (
  UserId int,
  Number int
)

create table Vals2 (
  UserId int,
  Number int
)

I have values in Vals1 and Vals2 but not a value for every UserId. What I want to do is, when available, sum the corresponding values between Vals1 and Vals2.

In this example, assume I have records with Id 1-5 in my User table. So, say I have the following in Vals1:

UserId     Number
1          10
2          15
4          20

And this in Vals2:

UserId     Number
1          30
2          55
3          40

This is what I want as output:

UserId       Number
1            40
2            70
3            40
4            20
5            0

My first stab at this produces correct results, but this seems really ugly:

;WITH AllVals1 AS (
  SELECT Id, ISNULL(Number, 0) as Number
  FROM Users
  LEFT JOIN Vals1 ON Id = UserId
), AllVals2 AS (
  SELECT Id, ISNULL(Number, 0) as Number
  FROM Users
  LEFT JOIN Vals2 ON Id = UserId
)
SELECT v1.Id, v1.Number + v2.Number
FROM AllVals1 v1
JOIN AllVals2 v2 ON v1.Id = v2.Id

Is there a more succinct/efficient way of doing this?

3 Answers 3

2

Here is much simpler way of doing it, this will only work if you have 1 row per ID in Vals1 and Vals2 table.

SELECT id, COALESCE(v1.Number, 0) + COALESCE(v2.Number, 0) as NumberSum
    FROM users u
    LEFT OUTER JOIN Vals1 AS v1
        ON u.id = v1.userid
    LEFT OUTER JOIN vals2 AS v2
        ON u.id = v2.userid

If you have more than 1 row per ID in values table than you can add SUM() and GROUP BY clause to get rid of multiple rows.

SELECT id
       ,SUM(COALESCE(v1.Number, 0) + COALESCE(v2.Number, 0))
    FROM users u
    LEFT OUTER JOIN Vals1 AS v1
        ON u.id = v1.userid
    LEFT OUTER JOIN vals2 AS v2
        ON u.id = v2.userid
    GROUP BY id
Sign up to request clarification or add additional context in comments.

2 Comments

Now test without the group by as my answer... what do you know... shocking! that works too.
@Hogan I updated my answer. Yes it does work as long as there is only 1 row per ID.
1

You can use a left join and then account for the nulls with coalesce or isnull

SELECT users.id, ISNULL(vals1.number,0) + ISNULL(vals2.number,0) as [sum]
FROM users
left join vals1 on vals1.userid = users.id
left join vals2 on vals2.userid = users.id

Comments

0

I suggest to first select all values in a derived temporary table and then calculate the sum grouping by user:

SELECT
    tmp.UserId,
    sum(tmp.Number) as Number
FROM
    (SELECT
        UserId,
        Number
    FROM
        Vals1

    UNION ALL

    SELECT
        UserId,
        Number
    FROM
        Vals2) tmp
GROUP BY
    tmp.UserId

5 Comments

This is horribly slow compared to doing two joins.
I don't think so because there is no need for a lookup. It simply returns all rows as your query would also as there is no value without a related user. You're query even has to read one more table AND do the joins.
you would not think so if you didn't know (as I do) that sql engines are much faster at doing joins than they are at doing UNION ALLs and GROUP BYs. But feel free to test it out... or work with SQL for 25 years as I have.
What about the execution plans? Have you tried this concrete use case right now (not in your long history)? Here are some other opinions voting for a UNION ALL: stackoverflow.com/questions/3374459/…
Did you read the page you linked -- union all is only faster when when you don't need the group by that is as they say when the two set don't intersect -- but please don't trust me... test it yourself. Then you can thank me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.