简体   繁体   中英

SUM of multiple rows of SUMs in MySQL

I have a query that returns a total column for every order :

SELECT o.id, o.created, o.status, o.shipping,
SUM(op.price*op.amount*(1+op.tax/100.0))+COALESCE(o.shipping, 0)*(1+o.shipping_tax/100.0)+COALESCE(o.payment_fee, 0)*(1+o.shipping_tax/100.0) AS total

FROM orders AS o
LEFT OUTER JOIN order_products as op ON o.id=op.order_id
GROUP BY o.id;

第一个查询的输出

Output of the query

Now I want to calculate the total revenue, so that would be the SUM of these total columns.

I tried using a subquery like this:

SELECT 
SUM(total) FROM (
    SELECT
    SUM(op.price*op.amount*(1+op.tax/100.0))+COALESCE(o.shipping, 0)*(1+o.shipping_tax/100.0)+COALESCE(o.payment_fee, 0)*(1+o.shipping_tax/100.0) AS total
    FROM orders AS o
    LEFT OUTER JOIN order_products as op ON o.id=op.order_id
    GROUP BY o.id
) AS foo
;

第二个查询的输出

Output of the query

However:

  • The result column was named SUM(total) , not foo
    • Seems like the AS foo is used for the subquery, not for the top-level SUM()
  • Without the AS foo , it doesn't work
  • The result that this query returned might have been incorrect, but that could have been due to rounding .

How should the query, to SUM multiple rows of SUMs, look?

Note: Those NULL totals almost certainly won't exist in production, but it'd still be better to use COALESCE(x, 0) just to make sure the query executes correctly. Seems like this SUM() is fine with NULL values?

when you want to alias name to total you have o set it before from not after your select and for checking null you have to set a value for that:

  SELECT 
    SUM(COALESCE(a.total,0)) as foo FROM (
        SELECT
        SUM(op.price*op.amount*(1+op.tax/100.0))+COALESCE(o.shipping, 0)*(1+o.shipping_tax/100.0)+COALESCE(o.payment_fee, 0)*(1+o.shipping_tax/100.0) AS total
        FROM orders AS o
        LEFT OUTER JOIN order_products as op ON o.id=op.order_id
        GROUP BY o.id
    ) AS a
SELECT
  SUM(
     COALESCE(op.order_total, 0)
    +COALESCE(o.shipping, 0)*(1+o.shipping_tax/100.0)
    +COALESCE(o.payment_fee, 0)*(1+o.shipping_tax/100.0)
  )
    AS grand_total
FROM
  orders   AS o
LEFT OUTER JOIN
(
  SELECT
    order_id,
    SUM(price*amount*(1+tax/100.0))  AS order_total
  FROM
    order_products
  GROUP BY
    order_id
)
  AS op
    ON o.id=op.order_id

EDIT:

You've said that :
- SUM(x+y+z) / COUNT(op.order_total) gives 506
- AVG(x+y+z) gives 532

This implies to me that some of the x+y+z rows are NULL when x is NOT NULL , therefore it appears that y or z are NULL .

As y and z are COALESCE(?, 0) * (1+o.shipping_tax/100.0) , it feels like shipping_tax is sometimes NULL .

Try this query...

SELECT
  SUM(
     COALESCE(op.order_total, 0)
    +COALESCE(o.shipping, 0)*(1+o.shipping_tax/100.0)
    +COALESCE(o.payment_fee, 0)*(1+o.shipping_tax/100.0)
  )
    AS grand_total,
  AVG(
     COALESCE(op.order_total, 0)
    +COALESCE(o.shipping, 0)*(1+o.shipping_tax/100.0)
    +COALESCE(o.payment_fee, 0)*(1+o.shipping_tax/100.0)
  )
    AS grand_average,
  COUNT(
     COALESCE(op.order_total, 0)
    +COALESCE(o.shipping, 0)*(1+o.shipping_tax/100.0)
    +COALESCE(o.payment_fee, 0)*(1+o.shipping_tax/100.0)
  )
    AS grand_row_count,
  COUNT(
    *
  )
    AS set_row_count,
  COUNT(
    o.shipping_tax
  )
    AS shipping_tax_row_count,
  AVG(
     COALESCE(op.order_total, 0)
    +(COALESCE(o.shipping, 0)+COALESCE(o.payment_fee, 0))
     *COALESCE(1+o.shipping_tax/100.0, 1)
  )
    AS revised_grand_average
FROM
  orders   AS o
LEFT OUTER JOIN
(
  SELECT
    order_id,
    SUM(price*amount*(1+tax/100.0))  AS order_total
  FROM
    order_products
  GROUP BY
    order_id
)
  AS op
    ON o.id=op.order_id

I expect that...
- grand_average == grand_total / grand_row_count
- set_row_count > grand_row_count
- grand_row_count == shipping_tax_row_count

If so, then the revised_grand_average should be useful to you.

If not, then hopefully this gives you a place to start investigating.

You don't need a subquery. Just remove the GROUP BY from the original query:

SELECT SUM(op.price*op.amount*(1+op.tax/100.0))+COALESCE(o.shipping, 0)*(1+o.shipping_tax/100.0)+COALESCE(o.payment_fee, 0)*(1+o.shipping_tax/100.0) AS total
FROM orders o LEFT OUTER JOIN
     order_products op
     ON o.id = op.order_id;

This will do the calculation over all the rows. It should be faster than two levels of aggregation.

The result will be in a column called total .

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