You might laugh when you here this, by there is an aggregate clause that triggers summary between breaks in values. The aggregrate clause is WITH ROLLUP
. (Look in MySQL Documentation under GROUP BY Modifiers)
let's take your query and make the following changes
- Remove
ORDER BY
- Substitute
WITH ROLLUP
- Remove
name
You get this
SELECT
cdr.datefield AS 'date',
a.id AS 'id',
SUM(t.debit_amount - t.credit_amount) AS 'balance'
FROM calendar cdr
JOIN transactions t ON (cdr.datefield >= t.value_date)
JOIN accounts a ON (a.id = t.account_id)
WHERE cdr.datefield IN ('2014-03-31', '2013-03-31', '2012-03-31')
GROUP BY cdr.datefield, a.id
WITH ROLLUP;
Here is what should happen
- In between each datefield, there will be
- datefield
id
NULL
- a sum of balance for that date
- The last row return
- datefield NULL
- id NULL
- a sum of balance for all dates
Based on the desired output, you want Cash
, Payables
and Issued Capital
as zero in case there is no transaction type for the given date. You also done want the last row because you don't want the grand total. What you do is take the new query, make it a subquery, and LEFT JOIN the accounts table to the subquery, returning zero for balance if it is NULL and exclude any row whose date is NULL.
Here is the new query
SELECT
BB.date,AA.id,AA.name,IFNULL(BB.balance,0) balance
FROM accounts AA LEFT JOIN
(SELECT
cdr.datefield AS 'date',
a.id AS 'id',
SUM(t.debit_amount - t.credit_amount) AS 'balance'
FROM calendar cdr
JOIN transactions t ON (cdr.datefield >= t.value_date)
JOIN accounts a ON (a.id = t.account_id)
WHERE cdr.datefield IN ('2014-03-31', '2013-03-31', '2012-03-31')
GROUP BY cdr.datefield, a.id
WITH ROLLUP) BB USING (id) WHERE AA.date IS NOT NULL
ORDER BY BB.date DESC,AA.id;
I have used WITH ROLLUP
to answer many posts in the DBA StackExchange
Give it a Try !!!
You can use conditional sum for this calculation something as
select
date_format(created,'%d-%m') as created_date ,
sum( case when status='open' then 1 else 0 end )+
sum( case when status='new' then 1 else 0 end )+
sum( case when status='resolved' and date(resolved) > date(created) then 1 else 0 end ) as open
from table_name
where
YEAR(Created) = '2015'
group by created_date ;
Here is a test case
mysql> select * from test ;
+------+---------------------+---------------------+----------+
| id | created | resolved | status |
+------+---------------------+---------------------+----------+
| 1 | 2015-05-10 00:00:00 | 1970-01-01 00:00:00 | open |
| 2 | 2015-05-10 00:00:00 | 1970-01-01 00:00:00 | new |
| 3 | 2015-05-10 00:00:00 | 2015-05-12 00:00:00 | resolved |
| 4 | 2015-05-11 00:00:00 | 1970-01-01 00:00:00 | open |
| 5 | 2015-05-11 00:00:00 | 1970-01-01 00:00:00 | new |
| 6 | 2015-05-11 00:00:00 | 2015-05-11 00:00:00 | resolved |
+------+---------------------+---------------------+----------+
6 rows in set (0.00 sec)
mysql> select
-> date_format(created,'%d-%m') as created_date ,
-> sum( case when status='open' then 1 else 0 end )+
-> sum( case when status='new' then 1 else 0 end )+
-> sum( case when status='resolved' and date(resolved) > date(created) then 1 else 0 end ) as open
-> from test
-> where
-> YEAR(Created) = '2015'
-> group by created_date ;
+--------------+------+
| created_date | open |
+--------------+------+
| 10-05 | 3 |
| 11-05 | 2 |
+--------------+------+
2 rows in set (0.00 sec)
UPDATE
The count of open is a incremental as per the comment. So the following should do the trick
select
created_date,
@open:= @open+open_ticket-resolved as open
from(
select
date_format(created,'%d-%m') as created_date ,
sum( case when status='open' then 1 else 0 end )+
sum( case when status='new' then 1 else 0 end )+
sum( case when status='resolved' and ( date(resolved) > date(created) or date(resolved) = date(created)) then 1 else 0 end ) as open_ticket,
sum(case when status = 'resolved' and date(resolved) = date(created) then 1 else 0 end) as resolved
from test
where
YEAR(Created) = '2015'
group by created_date
)x,(select @open:=0)r order by created_date ;
From the above sample data it will have
+--------------+------+
| created_date | open |
+--------------+------+
| 10-05 | 3 |
| 11-05 | 5 |
+--------------+------+
UPDATE:
Count the previous tickets which got resolved on a given date and deduct that from the current day open ticket, for that you need a left join
something as
select
created_date,
@open:= @open+open_ticket-resolved_ticket as open
from(
select
date_format(t.created,'%d-%m') as created_date ,
sum( case when t.status='open' then 1 else 0 end )+
sum( case when t.status='new' then 1 else 0 end )+
sum( case when t.status='resolved' and ( date(t.resolved) > date(created) or date(t.resolved) = date(created)) then 1 else 0 end ) as open_ticket,
coalesce(tot,0) as resolved_ticket
from test t left join(
select count(*) as tot, date(resolved) as resolved from test where status='resolved' group by date(resolved)
)x on date(x.resolved) = date(t.created)
where
YEAR(t.created) = '2015'
group by created_date
)x,(select @open:=0)r order by created_date ;
OUTPUT :
+--------------+------+
| created_date | open |
+--------------+------+
| 10-05 | 3 |
| 11-05 | 5 |
| 12-05 | 7 |
+--------------+------+
Best Answer
One way to solve such problems, is using window functions:
If there are ties, they will be resolved arbitrarily and only 2 rows per date will be returned. If you want to resolve ties with a specific preference, you can change the order inside the
OVER
clause. Say you want the products' names to be sorted alphabetically when they tie in top sales, use(... ORDER BY count DESC, product_name)
.If you want all the (tied) products per date, just change
ROW_NUMBER()
toRANK()
.