简体   繁体   中英

MySQL Joining multiple tables each with multiple rows

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM