【问题标题】:Sum columns from two tables in sql对sql中两个表的列求和
【发布时间】:2016-10-05 15:38:26
【问题描述】:

我有两张表,一张是cost 表,一张是payment 表,cost 表包含产品的成本和产品名称。

 Cost Table
  id    |   cost   |  name
   1    |   100    |   A
   2    |   200    |   B
   3    |   200    |   A


  Payment Table
  pid  | amount    | costID
  1    |   10      |   1
  2    |   20      |   1
  3    |   30      |   2
  4    |   50      |   1

现在我必须用相同的name 值对总成本求和,并且还要通过 costID 对总支付金额求和,如下面的查询

 totalTable

name | sum(cost)  |  sum(amount) |
  A  |  300       |     80       |
  B  |  200       |     30       |

但是我一直在使用下面的查询来解决这个问题,但我认为我做错了。

                SELECT 
                    b.name,
                    b.sum(cost),
                    a.sum(amount)

                FROM 
                      `Payment Table` a

                 LEFT JOIN
                      `Cost Table` b 
                ON   
                      b.id=a.costID


                      GROUP by b.name,a.costID

如果有人能帮助我解决我的问题,或者更好地了解如何解决这个问题,我将不胜感激。谢谢你

【问题讨论】:

  • 我认为您不需要在a.costID 上进行分组 - 如果您删除它,它会起作用吗?
  • 加入前需要“求和”。作为成本和付款 ic 之间的一对多连接,导致金额膨胀。
  • 名字A怎么有两个id?

标签: mysql sql


【解决方案1】:

这应该可行:

select t2.name, sum(t2.cost), coalesce(sum(t1.amount), 0) as amount
from (
   select id, name, sum(cost) as cost
   from `Cost`
   group by id, name
) t2
left join (
   select costID, sum(amount) as amount
   from `Payment`
   group by CostID
) t1 on t2.id = t1.costID
group by t2.name

SQLFiddle

【讨论】:

    【解决方案2】:

    您需要在单独的查询中进行计算,然后将它们连接在一起。

    • 第一个是直截了当的。
    • 第二个您需要获得与基于cost_id 的付款相关联的name

    SQL Fiddle Demo

    SELECT C.`name`, C.`sum_cost`, COALESCE(P.`sum_amount`,0 ) as `sum_amount`
    FROM (
        SELECT `name`, SUM(`cost`) as `sum_cost`
        FROM `Cost`
        GROUP BY `name`
        ) C
    LEFT JOIN (    
        SELECT `Cost`.`name`, SUM(`Payment`.`amount`) as `sum_amount`
        FROM `Payment`
        JOIN `Cost` 
           ON `Payment`.`costID` = `Cost`.`id`
        GROUP BY `Cost`.`name`
      ) P
      ON C.`name` =  P.`name`
    

    输出

    | name | sum_cost | sum_amount |
    |------|----------|------------|
    |    A |      300 |         80 |
    |    B |      200 |         30 |
    

    【讨论】:

    • 谢谢这个也很好用,谢谢你的努力+1
    • 我希望我能接受这两个答案,再次感谢
    • 您可以根据您认为能更好地解决您的问题来选择接受哪一个。我可以说我的答案更好,因为至少尝试解释正在发生的事情,而不是仅仅解释代码。即使当potashin say首先提交时,他也删除了他的第一个答案,因为是错误的。直到我的答案是正确的 4 分钟后的最终编辑。甚至他使用我的 sqlFiddle 来测试解决方案。
    【解决方案3】:

    几个问题。一方面,列引用应该是合格的,而不是聚合函数。

    这是无效的:

     table_alias.SUM(column_name) 
    

    应该是:

     SUM(table_alias.column_name)
    

    此查询应返回您要查找的前两列:

    SELECT c.name         AS `name`
         , SUM(c.cost)    AS `sum(cost)`
      FROM `Cost Table` c
     GROUP BY c.name
     ORDER BY c.name
    

    当您将连接引入另一个表时,例如Product Table,其中costid 不是唯一的,您有可能产生(部分)笛卡尔积。

    要查看它的外观和发生的情况,请删除 GROUP BY 和聚合 SUM() 函数,并查看使用连接操作的查询返回的详细信息行。

      SELECT c.id               AS `c.id`
           , c.cost             AS `c.cost`
           , c.name             AS `c.name`
           , p.pid              AS `p.pid`
           , p.amount           AS `p.amount`
           , p.costid           AS `p.costid`
        FROM `Cost Table` c
        LEFT
        JOIN `Payment Table` p
          ON p.costid = c.id
       ORDER BY c.id, p.pid
    

    这将返回:

     c.id | c.cost |  c.name | p.pid | p.amount | p.costid
     1    |    100 |  A      | 1     |       10 | 1       
     1    |    100 |  A      | 2     |       20 | 1       
     1    |    100 |  A      | 4     |       50 | 1       
     2    |    200 |  B      | 3     |       30 | 2
     3    |    200 |  A      | NULL  |     NULL | NULL
    

    请注意,我们从Cost Table 获得了三个 id=1 行的副本。

    因此,如果我们修改该查询,添加一个 GROUP BY c.name,并将 c.cost 包装在一个 SUM() 聚合中,我们将得到一个总 cost 的膨胀值。

    为了避免这种情况,我们可以从Payment Table 中聚合amount,这样每个costid 只能得到一行。然后,当我们执行连接操作时,我们不会从 Cost 生成重复的行副本。

    这是一个从Payment Table 聚合总金额的查询,因此我们为每个costid 获得一个 行。

      SELECT p.costid
           , SUM(p.amount) AS tot_amount
        FROM `Payment Table` p 
       GROUP BY p.costid
       ORDER BY p.costid
    

    那会返回:

      costid | tot_amount
      1      | 80
      2      | 30
    

    我们可以将该查询的结果当作一个表来使用,方法是将该查询设为“内联视图”。在此示例中,我们为查询结果分配了别名 v。 (在 MySQL venacular 中,“内联视图”称为“派生表”。)

    SELECT c.name                      AS `name`
         , SUM(c.cost)                 AS `sum_cost`
         , IFNULL(SUM(v.tot_amount),0) AS `sum_amount`
      FROM `Cost Table` c
      LEFT
      JOIN ( -- inline view to return total amount by costid 
             SELECT p.costid
                  , SUM(p.amount) AS tot_amount
               FROM `Payment Table` p
              GROUP BY p.costid
              ORDER BY p.costid
           ) v
        ON v.costid = c.id
     GROUP BY c.name
     ORDER BY c.name
    

    【讨论】:

    • 很好的解释。但是有两件事a)你需要使用LEFT JOIN,你可以使用Cost而不使用Payments。 b) 名称可以有不同的id,但不确定这是否是拼写错误。
    • @JuanCarlosOropeza:很好,我错过了最后一个查询的外连接(已修复)。还在 SUM() 周围添加了 IFNULL,以在付款中没有匹配的行时返回零来代替 NULL。我没有看到 nameid 的任何问题(可能是我错过了。围绕 OP 规范设计的查询似乎是 GROUP BY 名称,即使我们加入 c.id=p.costid
    • 我不确定,所以最好在 Fiddle 上测试一下。但是因为您要加入CostID,所以我担心[{name: A, Cost:1, Sum: SomeNumber}, {name: A, Cost:3, Sum: Null}group by name 将为空。
    • GROUP BY c.name 可以正常工作。 SUM(v.tot_amount) 会很好。这将包括 (1,3) 中具有成本行 id 的行,您是对的,id=3 的 tot_amount 将为 NULL,但 SUM() 聚合将处理 NULL 值。您正确地指出,如果我们要对 NULL 使用加法运算,例如800 + NULL,结果将是 NULL。但是SUM(v.tot_amount) 将是80。(如果我们使用COUNT(v.tot_amount),那将返回1。如果我们使用AVG(v.tot_amount),那将返回80。表达式AVG(IFNULL(v.tot_amount,0)) 将返回40。跨度>
    • 查询PaymentCost关联的行中省略的amount。例如如果Cost 中没有id=7 的行,Payment 中的一行像这样(pid=5,amount=420,costid=7) 将不匹配Cost 中的一行。使用外连接,这取决于我们希望哪个表成为驱动程序。上面的查询返回Cost 中的所有行,以及Payment 中的匹配行。 Payment 中与 Cost 中的行不匹配的行将被排除。
    猜你喜欢
    • 2018-03-22
    • 2020-01-29
    • 2021-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-24
    • 2013-10-01
    • 1970-01-01
    相关资源
    最近更新 更多