views:

121

answers:

7

Hi folks,

I have a database-table with the following structure:

id1 | id2 | weight

Now I want to sum up the weight for all entries, that have in id1 or id2 a given value.

SELECT id, SUM(weight) FROM myTable WHERE id1=@id or id2=@id

Usually, I need to get the sum for more than one id. To speed things up, I first load the ids for which the sum is needed in a temporary table. Now I do not know how to make the join, so that I get the sum for all ids. If only one column would be included, it would be

SELECT i.Id, SUM(weight)
FROM @IDs i JOIN myTable s1 ON i.Id=s1.id1 
GROUP BY i.Id

but then I would not get the entries where id2=@id. I could use a union in the following way:

SELECT i.Id, SUM(weight)
FROM @IDs i JOIN myTable s1 ON i.Id=s1.id1 
GROUP BY i.Id
UNION
SELECT i.Id, SUM(weight)
FROM @IDs i JOIN myTable s2 ON i.Id=s1.id2 
GROUP BY i.Id

but then I would get 2 sums, one cumulated over id1=@id and a second over id2=@id. But I want one result cumulated over both columns.

Anybody knows how to express this in SQL?

Thanks in advance, Frank

A: 

Quick and dirty would be to create a temporary table, insert the records from an id match into it, then insert the records from the id2 match, and then finally select the sum out of that temp table.

Jim B
If you're going to down-vote, you should at a minimum post a reason why
Jim B
How can I see who did the votes?
Aaginor
As far as i know, you can't
Jim B
Looks like someone is down-voting correct answers (not only this one) here without commenting and you can't do anything about it?
Aaginor
Most likely it's another person who answered here and is down-voting everybody else's.Just a nuance of the site I suppose. I can see not showing who did it, to prevent down-voting out of retaliation, but at the same time, I feel that if you do downvote, you should be required to post a justification of why.
Jim B
A: 
select t.id, Sum(s1.weight), Sum(s2.weight)
from table1 t
join table2 s1 on t.id1 = s1.id
join table2 s2 on t.id2 = s2.id
group by t.id

Edit: you'd prbably need the weight columns in the group by too.

Jimmeh
ACtually, i may have misunderstood the question slightly. I will try and repost, but i'm a little busy. Work always in the way :p
Jimmeh
A: 

why don't you extend your union solution in this way

select id,sum(s)
from(
  SELECT i.Id, SUM(weight) s
   FROM @IDs i JOIN myTable s1 ON i.Id=s1.id1 
   GROUP BY i.Id
   UNION
   SELECT i.Id, SUM(weight)
   FROM @IDs i JOIN myTable s2 ON i.Id=s1.id2 
   GROUP BY i.Id
)
group by id
catwalk
that's it, but I would us UNION ALL instead of UNION, because there may be the case that both weights for id1 and id2 are the same. Working with UNION would lose one of the weights.
Aaginor
A: 
SELECT a.ID, SUM(a.Weight) FROM
(SELECT i.Id, weight
FROM @IDs i 
JOIN myTable s1 
 ON i.Id=s1.id1 
GROUP BY i.Id
UNION
SELECT i.Id, weight
FROM @IDs i 
JOIN myTable s2 
 ON i.Id=s1.id2 
GROUP BY i.Id) a

IF I understand you corectly this might be what you want.

HLGEM
same as in my comment to catwalk: That's it, but with UNION ALL instead of UNION
Aaginor
+3  A: 
SELECT  id, SUM(weight)
FROM    (
        SELECT  i.id, s1.weight
        FROM    @IDs i
        JOIN    myTable s1
        ON      s1.id1 = i.id
        UNION ALL
        SELECT  i.id, s1.weight
        FROM    @IDs i
        JOIN    myTable s1
        ON      s1.id2 = i.id
        ) q
GROUP BY
        id

or this:

SELECT  i.id, SUM(CASE WHEN id = id1 THEN weight ELSE 0 END + CASE WHEN id = id2 THEN weight ELSE 0 END)
FROM    @ids i
JOIN    mytable s
ON      i.id IN (s.id1, s.id2)
GROUP BY
        id

The first one is more efficient if few records in mytable satisfy the conditions, the second one is more efficient if many records do.

Quassnoi
thanks! this works fine (I tried the second one)
Aaginor
After testing the queries with some real data I found that the second query runs VERY slow (~5 Min for 55 entries in @IDs an ~9k entries that fit the joins). I played a bit around and found that this very slow query is caused by the ON-clause. IN is bad as well as combining both ids with OR. However, the first query with UNION ALL runs below 1 second!
Aaginor
`@aaginor`: how many records are there in the table?
Quassnoi
over all? about 10-15 millions on average. I have some different tables with the same structure, the biggest table contains 27.8 million entries
Aaginor
`@aaginor`: that's right then. The first query does use the index and is more efficient, since relatively few records satisfy the join condition.
Quassnoi
+1  A: 
SELECT
     MT.id1,
     MT.id2,
     SUM(MT.weight)
FROM
     @IDs IDs
INNER JOIN My_Table MT ON
     MT.id1 = IDs.id OR
     MT.id2 = IDs.id
GROUP BY
     id1,
     id2

Or if you want the weights double-counted (once for id1 and once for id2) then:

SELECT
     IDs.id,
     SUM(MT.weight)
FROM
     @IDs IDs
INNER JOIN My_Table MT ON
     MT.id1 = IDs.id OR
     MT.id2 = IDs.id
GROUP BY
     id1,
     id2
Tom H.
A: 

I think you just need to add an OR to your JOIN clause:

SELECT i.Id, SUM(weight)
FROM @IDs i JOIN myTable s1 ON i.Id=s1.id1 OR i.Id=s1.id2
GROUP BY i.Id
gkrogers