【问题标题】:MySQL Query: SUM counting duplicate rowsMySQL 查询:SUM 计数重复行
【发布时间】:2017-05-24 20:52:35
【问题描述】:

我有一个为我们的客户生成损益表的查询,但我在加入订单表时遇到问题,导致 SUM 乘以费用条目的数量。

我有以下表格:

  • user_report_categories,“urc”,即举报用户的类别标题
  • user_report_expenses,“ure”,即个人费用,带有“adjusted_cost”和“expense_date”。这些可能有也可能没有与之相关的订单,但在这种情况下,我们只关心与订单相关的费用。每个订单可能有多项费用,但每个费用/订单总是属于不同的类别。
  • orders,“o”,其中包含“agreed_fee”、“balance”,当然还有“id”

报告需要在用户选择的日期范围内每月有一列,单元格中包含计算值。

查询如下:

SELECT
SUBSTRING(MONTHNAME(STR_TO_DATE(m, '%m')), 1, 3) AS month,
COUNT(o.id) AS "# of Orders",
ROUND(SUM(o.agreed_fee) - SUM(o.balance), 2) AS "Total Income",
ROUND(SUM(ure.adjusted_cost), 2) AS "Total Expenses",
ROUND(SUM(o.agreed_fee) - SUM(o.balance) - SUM(ure.adjusted_cost), 2) AS "Profit & Loss",
"" AS "",
ROUND(SUM(ure.adjusted_cost) / COUNT(ure.id), 2) AS "% of Expenses per Order",
ROUND((SUM(o.agreed_fee) - SUM(o.balance)) / COUNT(ure.id), 2) AS "Average Fee per Order",
ROUND(((SUM(o.agreed_fee) - SUM(o.balance)) / COUNT(ure.id)) - (SUM(ure.adjusted_cost) / COUNT(ure.id)), 2) AS "Average P/L per Order"

FROM ( 
    SELECT y, m FROM 
    (SELECT YEAR('2016-01-01') y) years, 
    (SELECT 1 m UNION ALL SELECT 2 UNION ALL SELECT 3) months
) ym 

LEFT JOIN user_report_categories AS urc ON urc.user_id = 48 
LEFT JOIN user_report_entries AS ure ON ure.user_category_id = urc.id AND YEAR(ure.expense_date) = y AND MONTH(ure.expense_date) = m 
LEFT JOIN orders AS o ON o.id = ure.order_id 
WHERE urc.report_type = 'expense' AND urc.user_id = 48  AND ure.order_id IS NOT NULL
GROUP BY y, m

结果:

month,# of Orders,Total Income,Total Expenses,Profit & Loss,,% of Expenses per Order,Average Fee per Order,Average P/L per Order
Jan,387,36400.00,5921.17,30478.83,,15.30,94.06,78.76
Feb,559,55327.50,8165.12,47162.38,,14.61,98.98,84.37
Mar,736,74785.00,10261.07,64523.93,,13.94,101.61,87.67

我通过将订单 ID 添加到 group-by 来确定订单乘以费用数量

GROUP BY y, m, o.id

并查看每行有多个订单的新结果:

month,# of Orders,Total Income,Total Expenses,Profit & Loss,,% of Expenses per Order,Average Fee per Order,Average P/L per Order
Jan,6,360.00,31.95,328.05,,5.33,60.00,54.68
Jan,1,0.00,30.24,-30.24,,30.24,0.00,-30.24
Jan,6,1200.00,141.74,1058.26,,23.62,200.00,176.38
Jan,6,540.00,160.97,379.03,,26.83,90.00,63.17
Jan,6,540.00,98.77,441.23,,16.46,90.00,73.54
Jan,8,720.00,167.44,552.56,,20.93,90.00,69.07
... etc ...

或者,当我从 group-by 中删除 o.id 并改为将 # of orders 行更改为不同时:

COUNT(DISTINCT o.id) AS "# of Orders",

我得到了订单 # 的正确值,但由于重复,当然 SUM 订单表值的其他值仍然不正确。

month,# of Orders,Total Income,Total Expenses,Profit & Loss,,% of Expenses per Order,Average Fee per Order,Average P/L per Order
Jan,71,36400.00,5921.17,30478.83,,15.30,94.06,78.76
Feb,105,55327.50,8165.12,47162.38,,14.61,98.98,84.37
Mar,146,74785.00,10261.07,64523.93,,13.94,101.61,87.67

我不确定我是否对此采取了一种好的方法,但是在一个查询中执行这一代对我来说是一个巨大的优势,所以我试图将它拼凑起来。我怎样才能得到它对不同订单 ID 的 SUM 订单表值,或者更正查询以正确计算?谢谢!


回答

@Sal 把我赶走了,我最后的询问如下。我认为它也涵盖了 cmets 中出现的所有问题。

SELECT 
SUBSTRING(MONTHNAME(STR_TO_DATE(m, '%m')), 1, 3) AS month,
orders AS "# of Orders",
round(total_income,2) AS "Total Income",
round(total_expenses,2) AS "Total Expenses",
round(total_income-total_expenses,2) AS "Profit & Loss",
"" AS "",
round(total_expenses/orders,2) AS "% of Expenses per Order",
round(total_income/orders,2) AS "Average Fee per Order",
round( (total_income/orders)-(total_expenses/orders), 2) AS "Average P/L per Order"

        FROM ( 
            SELECT m, 
            (SELECT count(o.id)
                FROM orders o
                WHERE year(o.datetime) = ym.y
                AND month(o.datetime) = ym.m AND o.user_id = 48
                AND o.cancelled = 0
            ) AS orders,
            (SELECT IFNULL(sum(o.agreed_fee - o.balance), 0)
                FROM orders AS o
                WHERE year(o.datetime) = ym.y
                AND month(o.datetime) = ym.m 
                AND o.user_id = 48
                AND o.cancelled = 0
            ) AS total_income,
            (SELECT IFNULL(sum(ure.adjusted_cost),0)
                FROM user_report_entries AS ure
                INNER JOIN user_report_categories AS urc ON urc.id = ure.user_category_id
                  WHERE year(ure.expense_date) = ym.y
                AND month(ure.expense_date) = ym.m 
                AND urc.user_id = 48  AND urc.id NOT IN (6287) ) AS total_expenses
            FROM ( SELECT 2017 y, 1 m  UNION SELECT 2017, 2 UNION SELECT 2017, 3
            ) ym

            GROUP BY y, m
        ) t

【问题讨论】:

  • COUNT(o.id) with GROUP BY, COUNT(o.id) without GROUP BYCOUNT(DISTINCT) 彼此不同,将返回不同的值。根据上面的查询,很难理解预期的输出是什么,所以最好添加更多信息(即表模式和/或输出)或者只是一个SQLFiddle
  • 我感觉到一个问题:user_report_entries 有成本,并且与订单有关。假设您有一个 agreed_fee 为 100 的订单。对于此订单,您有一个报告条目 2016-01,adjusted_cost 为 40,另一个报告条目 2016-02,adjusted_cost 为 50。所以你用哪个agreed_fee计算两个月中的哪一个?您需要一个算法(可能仅在第一个或最后一个报告条目的月份或订单日期的月份计算订单及其所有报告条目)。
  • @DarshanMehta 这些是我尝试过的一些事情及其结果。我需要查询的行为方式是每个订单只计算或汇总一次,即使每个订单有多项费用。
  • @ThorstenKettner 你是对的,这是我查询中的一个大缺陷。由于订单代表收入,因此应在执行当月使用,在创建当月使用费用。我将不得不对此做更多的计算。

标签: mysql count sum left-join


【解决方案1】:

我希望你觉得这很有用:

SELECT substring( monthname( str_to_date(m,'%m'), 1, 3) month
     , orders "# of Orders"
     , round(total_income,2) "Total Income"
     , round(total_expenses,2) "Total Expenses"
     , round(total_income-total_expenses,2) "Profit & Loss"
     , round(total_expenses/orders,2) "% of Expenses per Order"
     , round(total_income/orders,2) "Average Fee per Order"
     , round( (total_income/orders)-(total_expenses/orders), 2) "Average P/L per Order"
  FROM ( SELECT m
              , ( SELECT count(distinct ure.order_id)
                    FROM user_report_entries ure
                    WHERE year(ure.expense_date) = ym.y
                      AND month(ure.expense_date) = ym.m
                ) orders
              , ( SELECT sum(o.agreed_fee - o.balance)
                    FROM user_report_entries ure
                      INNER JOIN orders o
                        ON o.id = ure.order_id             
                    WHERE year(ure.expense_date) = ym.y
                      AND month(ure.expense_date) = ym.m 
                ) total_income
              , sum(ure.adjusted_cost) total_expenses
           FROM ( SELECT 2016 y, 1 m
                  UNION
                  SELECT 2016, 2
                  UNION
                  SELECT 2016, 3
                ) ym
             LEFT JOIN user_report_entries ure
               ON    year(ure.expense_date) = ym.y
                 AND month(ure.expense_date) = ym.m
             LEFT JOIN user_report_categories urc
               ON    urc.id = ure.user_category_id
           WHERE urc.user_id = 48
             AND urc.report_type = 'expense'
           GROUP BY y, m
       ) t

【讨论】:

  • 这让我朝着正确的方向前进,我稍微调整了一下,现在可以正常工作了,谢谢!
猜你喜欢
  • 2011-04-10
  • 1970-01-01
  • 1970-01-01
  • 2018-12-25
  • 1970-01-01
  • 1970-01-01
  • 2014-10-23
  • 1970-01-01
相关资源
最近更新 更多