Reputation: 1288
I thought that this would be an easy one but it baffles me and I don't know the proper solution. Let me represent the problem. Here are the tables and the data:
create table table1 (ID int primary key, total int);
insert into table1 values (1, 100);
insert into table1 values (2, 150);
create table table2 (ID int primary key, table1_ID int, total int);
insert into table2 values(1, 1, 10);
insert into table2 values(2, 1, 20);
insert into table2 values(3, 1, 40);
create table table3 (ID int primary key, table2_ID int, total int);
insert into table3 values(1, 1, 1000);
insert into table3 values(2, 1, 1000);
insert into table3 values(3, 2, 2000);
insert into table3 values(4, 2, 2000);
I need to write a SQL query which will list table1 along with summed totals for table 2 and table 3 if there is any totals.
If it's only table1 and table2 then this query works fine:
select table1.ID, table1.total, sum(table2.total) from table1
left outer join table2 on table1.ID = table2.table1_ID
group by table1.ID, table1.total;
The results are correct:
1 100 70
2 150 null
But, if I try to include table3:
select table1.ID, table1.total, sum(table2.total), sum(table3.total) from table1
left outer join table2 on table1.ID = table2.table1_ID
left outer join table3 on table2.ID = table3.table2_ID
group by table1.ID, table1.total
I get this result:
1 100 100 6000
2 150 null null
Sum of table 2 is not correct because join with table 3 multiplied its rows and the value 10 and value 20 were summed twice. How can I correct this SQL query?
In my real life example I actually have table4 and table5.
Upvotes: 1
Views: 2149
Reputation: 1136
for sql server 2012
DECLARE @x table(id int,total int,stotal int)
INSERT INTO @x
SELECT id,total,sum(total)
FROM table1
UNION
SELECT id,total,sum(total)
FROM table2
UNION
SELECT id,total,sum(total)
FROM table3
SELECT id,total,lead(sum(stotal))
FROM @x
Upvotes: 0
Reputation: 1210
SELECT
table1.ID,
table1.total,
total2,
total3
FROM
table1
CROSS APPLY
(
SELECT
SUM(table2.total) as total2
FROM
table2
WHERE
table1_ID = table1.ID
) AS T1
CROSS APPLY
(
SELECT
SUM(table3.total) as total3
FROM
table3
INNER JOIN
table2
ON
table3.table2_ID = table2.ID
WHERE
table2.table1_ID = table1.ID
) AS T2
Upvotes: 0
Reputation: 1270573
You need to pre-aggregate the values along each dimension. You are getting a cartesian product, which throws off the sums:
select t1.ID, t1.t1_total, t2.t2_total, t3.t3_total
from table1 t1 left join
(select t2.table1_id, sum(t2.total) as t2_total
from table2 t2
group by t2.table1_id
) t2
on t1.id = t2.table1_id left join
(select t2.table1_id, sum(t3.total) as t3_total
from table2 t2 join
table3 t3
on t2.id = t3.table2_id
group by t2.table1_id
) t3
on t1.id = t3.table1_id;
In other words, get everything to the level of table1.id
before doing the joins, so the join does not multiply the number of rows unnecessarily.
Upvotes: 2
Reputation: 29051
Try this:
SELECT t1.ID, t1.total,
SUM(ISNULL(t2.total, 0)) AS t2Total,
SUM(ISNULL(t3.total, 0)) AS t3Total
FROM table1 t1
LEFT OUTER JOIN table2 t2 ON t1.ID = t2.table1_ID
LEFT OUTER JOIN (SELECT t3.table2_ID, SUM(t3.total) AS total
FROM table3 t3
GROUP BY t3.table2_ID
) AS t3 ON t2.ID = t3.table1_ID
GROUP BY t1.ID, t1.total;
Upvotes: 1