I have tables (bar, baz) which have 1 or more rows relating to another table (foo). When I join both bar & baz to foo I get results for each row of each table.
http://sqlfiddle.com/#!9/1c13f2/1/0
CREATE TABLE foo (`id` int, `value` varchar(5));
INSERT INTO foo (`id`, `value`) VALUES
(1, 'two'),
(2, 'two'),
(3, 'one');
CREATE TABLE bar (`id` int, `foo_id` int, `value` int);
INSERT INTO bar (`id`, `foo_id`, `value`) VALUES
(1, 1, 1),
(2, 1, 1),
(3, 2, 1),
(4, 2, 1),
(5, 3, 1);
CREATE TABLE baz (`id` int, `foo_id` int, `value` int);
INSERT INTO baz (`id`, `foo_id`, `value`) VALUES
(1, 1, 1),
(2, 1, 1),
(3, 2, 1),
(4, 2, 1),
(5, 3, 1);
The query:
SELECT foo.value, SUM(bar.value), SUM(baz.value)
FROM foo
JOIN bar ON bar.foo_id = foo.id
JOIN baz ON baz.foo_id = foo.id
GROUP BY foo.id
Result:
value SUM(bar.value) SUM(baz.value)
two 4 4
two 4 4
one 1 1
Expected result:
value SUM(bar.value) SUM(baz.value)
two 2 2
two 2 2
one 1 1
The result is the expected behavior, from a cross (semi-Cartesian) product, multiple rows from bar
matched to multiple rows from baz
.
To avoid this, we can pre-aggregate counts from bar
and baz
, and then do the join.
Also consider, what result is expected when there are matching rows in bar
but no matching rows in baz
. Do we want to return the total from bar
? With the current query, we wouldn't get total from bar
. (In the example data, consider what the query will return after row id=5 is deleted from baz
.)
I'd write the query like this:
SELECT foo.value
, IFNULL( r.tot_bar_value ,0) AS tot_bar_value
, IFNULL( z.tot_baz_value ,0) AS tot_baz_value
FROM foo
LEFT
JOIN ( -- aggregate total from bar
SELECT bar.foo_id
, SUM(bar.value) AS tot_bar_value
FROM bar
GROUP BY bar.foo_id
) r
ON r.foo_id = foo.id
LEFT
JOIN ( -- aggregate total from bar
SELECT baz.foo_id
, SUM(baz.value) AS tot_baz_value
FROM baz
GROUP BY baz.foo_id
) z
ON z.foo_id = foo.id
Note that we are using outer joins, to handle the case when there not matching rows in either bar
or baz
.
For testing, we can run separately just the SELECT query inside the parens, to see what is returned.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.