как получить сумму двух таблиц, связанных с третьей - PullRequest
2 голосов
/ 09 ноября 2011

Помогут ли здесь какие-либо Wizards sql с этим вопросом: Предположим, у меня есть 3 таблицы:

tbltype        tblvalue               tblcost
id  | type     id  | val | typeid     id  | cost| typeid
----------     ------------------     ------------------ 
1   | aaa      1   | 3   | 1          1   | 5   | 1 
2   | bbb      2   | 2   | 1          2   | 3   | 1
               3   | 2   | 2          3   | 1   | 2
               4   | 1   | 2          4   | 4   | 2

Когда я запускаю этот запрос:

SELECT t.type, SUM(val), SUM(cost)
FROM
    tbltype t 
    LEFT JOIN tblcost c ON (c.typeid = t.id)
    LEFT JOIN tblvalue v ON (v.typeid = t.id)
GROUP BY t.type;

Я получил неправильное значение

type | SUM(val) | SUM(cost)
---------------------------
aaa  | 10       | 16
bbb  | 6        | 10

как мне получить правильное значение:

type | SUM(val) | SUM(cost)
---------------------------
aaa  | 5        | 8
bbb  | 3        | 5

а почему sql так себя ведет?

Ответы [ 5 ]

3 голосов
/ 09 ноября 2011

Чтобы понять, почему, возьмите группу и суммы из вашего запроса и посмотрите, каково это суммирование:

SELECT t.type, val, cost
FROM
    tbltype t 
    LEFT JOIN tblcost c ON (c.typeid = t.id)
    LEFT JOIN tblvalue v ON (v.typeid = t.id)

Вы увидите, что у вас есть все возможные комбинации строк из tblcost и tblvalue вoutput-- это означает, что некоторые из них подсчитываются несколько раз, когда вы их суммируете.

Вам необходимо агрегировать tblcost и tblvalue отдельно.Затем вы можете присоединиться к ним обратно на tbltype.Ответ Гэвина уже показывает один из способов сделать это.Другой способ:

SELECT t.type, COALESCE(cost, 0) AS cost, COALESCE(val, 0) AS val
FROM tbltype t
     LEFT JOIN (SELECT SUM(cost) AS cost, typeid FROM tblcost GROUP BY typeid) tc
       ON tc.typeid = t.id
     LEFT JOIN (SELECT SUM(val) AS val, typeid FROM tblvalue GROUP BY typeid) tv
       ON tv.typeid = t.id

... который может работать, а может и не работать по-разному (а может и не быть лучше), в зависимости от того, какой механизм базы данных вы фактически используете.

3 голосов
/ 09 ноября 2011
SELECT t.type,
       COALESCE((SELECT SUM(v.val) FROM tblvalue AS v WHERE v.typeid = t.id),0) AS val,
       COALESCE((SELECT SUM(c.cost) FROM tblcost AS c WHERE c.typeid = t.id),0) AS cost
  FROM tbltype AS t;
2 голосов
/ 09 ноября 2011

Я думаю, что у вас достаточно ответов, подсказывающих, как правильно решить вашу проблему. Вы также получили ответ @ araqnid, который поможет вам понять, почему вы в итоге получаете такие результаты. Похоже, мне остается только объяснить само поведение в соответствии с вашей просьбой.

По сути, причина такого поведения заключается в том, что второе соединение выполняется не tbltype и tblvalue, как можно подумать, а в результате объединения между tbltype и tblcost с одной стороны, и стол tblvalue, с другой. Теперь при первом объединении создаются дубликаты t.id, поскольку они соответствуют второй таблице более одного раза:

tbltype        tblcost
id  type       id  cost  typeid       t.id  t.type  c.id  c.cost  c.typeid
--  ----   ×   --  ----  ------   =   ----  ------  ----  ------  --------
1   aaa        1   5     1            1     aaa     1     5       1
1   bbb        2   3     1            1     aaa     2     3       1
               3   1     2            2     bbb     3     1       2
               4   4     2            2     bbb     4     4       2

Второе объединение создает больше дубликатов, потому что:

  • каждое вхождение t.id из набора результатов первого объединения сопоставляется с v.typeid

и

  • значения typeid в таблице tblvalue также дублируются.

В результате строки из tblcost и tblvalue дублируются в процессе:

                                           tblvalue        
t.id  t.type  c.id  c.cost  c.typeid       id  val  typeid  
----  ------  ----  ------  --------       --  ---  ------  
1     aaa     1     5       1          ×   1   3    1        =
1     aaa     2     3       1              2   2    1       
2     bbb     3     1       2              3   2    2       
2     bbb     4     4       2              4   1    2       


    t.id  t.type  c.id  c.cost  c.typeid  v.id  v.val  v.typeid
    ----  ------  ----  ------  --------  ----  -----  --------
    1     aaa     1     5       1         1     3      1
    1     aaa     1     5       1         2     2      1
=   1     aaa     2     3       1         1     3      1
    1     aaa     2     3       1         2     2      1
    2     bbb     3     1       2         3     2      2
    2     bbb     3     1       2         4     1      2
    2     bbb     4     4       2         3     2      2
    2     bbb     4     4       2         4     1      2

Единственным выходом для вас, похоже, является агрегирование каждой таблицы отдельно. Это не обязательно подразумевает отдельные запросы, просто отдельные sub запросы, как теперь видно из ответов.

0 голосов
/ 09 ноября 2011

Примерно так:

select t1.id, val, cost from (
    select t.id, sum(val) as val
    from tbltype t
    join tblvalue v on t.id = v.typeId
    group by t.id
) t1
join (
    select t.id, sum(cost) as cost
    from tbltype t
    inner join tblcost c on t.id = c.typeid
    group by t.id
) t2 on t1.id = t2.id

... или если tblcost и tblvalue связаны id:

select t.id, sum(val) as val, sum(cost) as cost
from tbltype t
inner join tblcost c on t.id = c.typeid
inner join tblvalue v on c.id = v.id
group by t.id
0 голосов
/ 09 ноября 2011

Быстрое решение состоит в том, чтобы разделить запрос на две

SELECT
    `tbltype`.`type`, SUM(val)
FROM
    tbltype
     LEFT JOIN `tblvalue` ON (`tblvalue`.`typeid` = `tbltype`.`id`)
GROUP BY `tbltype`.`type`;

и

SELECT
    `tbltype`.`type`, SUM(cost)
FROM
    tbltype
     LEFT JOIN `tblcost` ON (`tblcost`.`typeid` = `tbltype`.`id`)
GROUP BY `tbltype`.`type`;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...