Your question shows you are thinking in a procedural way, however you need to think in a "set-based" way.
The following query may help you understand this better:
SELECT p.customer_id
, c.name
, sum(t.points)
FROM products p
INNER JOIN types t ON p.type_id = t.id
INNER JOIN categories c ON t.category_id = c.id
GROUP BY p.customer_id
, c.name;
Essentially, this should show you a list of customer_id, category name, and the total points each customer has in each category. It accomplishes this through the use of an aggregate function (SUM
) along with the GROUP BY
clause.
(please take a look at my answer here for information about naming your ID
columns)
Unfortunately, MySQL is lacking window or analytic functions which make complex grouping queries easier. For your use case, though, it should be possible to get it working with a single query.
Doing a simple row-based group of your sample dataset:
MySQL [test]> select orderId , category, count(*), sum(subtotal) from OrderLineItems group by 1,2;
+---------+-----------+----------+---------------+
| orderId | category | count(*) | sum(subtotal) |
+---------+-----------+----------+---------------+
| 323607 | BUILD | 5 | 2515.00 |
| 323607 | FINISHING | 1 | 30.00 |
| 323607 | SETUP | 2 | 95.00 |
| 323614 | BUILD | 4 | 4614.00 |
| 323614 | SETUP | 1 | 36.00 |
| 323636 | BUILD | 2 | 840.00 |
| 323636 | FINISHING | 1 | 130.00 |
| 323636 | SETUP | 7 | 1152.00 |
| 323649 | BUILD | 1 | 240.00 |
| 323649 | FINISHING | 1 | 30.00 |
| 323649 | SETUP | 4 | 1995.00 |
+---------+-----------+----------+---------------+
11 rows in set (0.00 sec)
You can pivot the subtotals to columns using the trick of using an IF statement in the select. This way you get the main part of your data with only one query:
SELECT orderId , count(*),
sum(if(category='BUILD', subtotal, 0)) as Build_Total,
sum(if(category='SETUP', subtotal, 0)) as Setup_Total,
sum(if(category='FINISHING', subtotal, 0)) as Finishing_Total,
sum(subtotal) as Total
from OrderLineItems
GROUP BY 1;
With output:
+---------+----------+-------------+-------------+-----------------+---------+
| orderId | count(*) | Build_Total | Setup_Total | Finishing_Total | Total |
+---------+----------+-------------+-------------+-----------------+---------+
| 323607 | 8 | 2515.00 | 95.00 | 30.00 | 2640.00 |
| 323614 | 5 | 4614.00 | 36.00 | 0.00 | 4650.00 |
| 323636 | 10 | 840.00 | 1152.00 | 130.00 | 2122.00 |
| 323649 | 6 | 240.00 | 1995.00 | 30.00 | 2265.00 |
+---------+----------+-------------+-------------+-----------------+---------+
4 rows in set (0.00 sec)
You can use the same approach to get your date-based rollups.
Best Answer
In case you wonder, why I use this
case when
construct, please refer to this post.P.S.: When using variables like this, you should have a
order by
clause.Group by
in MySQL uses an implicitorder by
when none is specified. But remember usingorder by
when you don't have agroup by
.EDIT: Actually your
group by
doesn't fit here, it seems to me. I changed it to aorder by
. And I changed your points column to datatype decimal(5,2), cause you have.5
in your sample data, which would become1
when you continue to useint
.EDIT:
To get the total for each agent, just wrap above query in another query like this: