Summarizing data by current and previous month from one table - mysql

I am looking to summarize data by this and previous month. My table looks like this:
Date Client Amount
2019-06-02 111 100
2019-06-03 111 90
2019-06-22 222 80
2019-07-02 111 110
2019-07-03 111 120
Expected result:
Client This_month Previous_month
111 230 190
222 80
Thank you for any hints how to do it.

You can group by client and use conditional aggregation:
select
client,
sum(case when last_day(date) = last_day(current_date) then amount end) This_month,
sum(case when last_day(date) = last_day(current_date - INTERVAL 1 MONTH) then amount end) Previous_month
from tablename
group by client
See the demo.
Results:
| client | This_month | Previous_month |
| ------ | ---------- | -------------- |
| 111 | 230 | 190 |
| 222 | | 80 |

You could try using a pair of left join
select a.Client, b.act_amount, c.prev_amount
from (
select distinct client
from my_table
) a
left join (
select month(date) act_mont, client, sum(amount) act_amount
from my_table
where month(date) = month(curdate)
group by month(date), client
) b on a.client = b.client
left join (
select month(date) prev_mont, client, sum(amount) prev_amount
from my_table
where month(date) = month(curdate) -1
group by month(date), client
) c on a.client = c.client

Related

Mysql: Fill column with consecutive numbers of days in a month

I have this table: "sales"
+-------------+---------+
| date | total |
+-------------+---------+
| 2018-12-04 | 269.10 |
| 2018-12-05 | 29.00 |
| 2018-12-06 | 107.10 |
| 2018-12-06 | 34.00 |
| 2018-12-08 | 69.50 |
| 2018-12-08 | 223.00 |
| 2018-12-08 | 68.00 |
| 2018-12-09 | 99.00 |
| 2018-12-10 | 59.50 |
| ... | ... |
+-------------+---------+
I'm trying this query
SELECT DAY(date) AS Days,
SUM(CASE WHEN MONTH(date) = 12 THEN total ELSE NULL END) AS December
FROM sales WHERE YEAR(date) = 2018 GROUP BY date
And I get
+-------+----------+
| Days | December |
+-------+----------+
| 4 | 269.10 |
| 5 | 29.00 |
| 6 | 141.10 |
| 8 | 360.50 |
| 9 | 99.00 |
| 10 | 59.50 |
| ... | ... |
+-------+----------+
But I want consecutive days like this:
+-------+----------+
| Days | December |
+-------+----------+
| 1 | NULL |
| 2 | NULL |
| 3 | NULL |
| 4 | 269.10 |
| 5 | 29.00 |
| 6 | 141.10 |
| 7 | NULL |
| 8 | 360.50 |
| 9 | 99.00 |
| 10 | 59.50 |
| ... | ... |
| 31 | 123.00 |
+-------+----------+
Can you help me plss..
PS: I have several months and years in "date" column from "sales" table.
This recursive CTE generates a list of dates corresponding to the month and year specified in the doi CTE, and then LEFT JOINs that to the sales table to get the sales for that month. It will work for any month/year, just change the values in the doi CTE, and the title of the SUM column (currently December) to suit.
WITH RECURSIVE doi AS (
SELECT 12 AS month,
2018 AS year
),
cte AS (
SELECT DATE(CONCAT_WS('-', year, month, 1)) AS date
FROM doi
UNION ALL
SELECT date + INTERVAL 1 DAY
FROM cte
WHERE date < LAST_DAY(date)
)
SELECT DAY(cte.date) AS Days,
ROUND(SUM(s.total),2) AS December
FROM cte
LEFT JOIN sales s ON s.date = cte.date
GROUP BY cte.date
ORDER BY cte.date
Output is too long to show here but can be seen at this demo on dbfiddle
Update
To expand this query to cover an entire year requires changing the approach slightly in terms of generating an entire year's worth of dates, and then using conditional aggregation to get the sums for each day of each month:
WITH RECURSIVE doi AS (
SELECT 2018 AS year
),
cte AS (
SELECT DATE(CONCAT_WS('-', year, 1, 1)) AS date
FROM doi
UNION ALL
SELECT date + INTERVAL 1 DAY
FROM cte
CROSS JOIN doi
WHERE date < DATE(CONCAT_WS('-', doi.year, 12, 31))
)
SELECT DAY(cte.date) AS Days,
ROUND(SUM(CASE WHEN MONTH(s.date) = 1 THEN s.total END),2) AS January,
ROUND(SUM(CASE WHEN MONTH(s.date) = 2 THEN s.total END),2) AS February,
ROUND(SUM(CASE WHEN MONTH(s.date) = 3 THEN s.total END),2) AS March,
ROUND(SUM(CASE WHEN MONTH(s.date) = 4 THEN s.total END),2) AS April,
ROUND(SUM(CASE WHEN MONTH(s.date) = 5 THEN s.total END),2) AS May,
ROUND(SUM(CASE WHEN MONTH(s.date) = 6 THEN s.total END),2) AS June,
ROUND(SUM(CASE WHEN MONTH(s.date) = 7 THEN s.total END),2) AS July,
ROUND(SUM(CASE WHEN MONTH(s.date) = 8 THEN s.total END),2) AS August,
ROUND(SUM(CASE WHEN MONTH(s.date) = 9 THEN s.total END),2) AS September,
ROUND(SUM(CASE WHEN MONTH(s.date) = 10 THEN s.total END),2) AS October,
ROUND(SUM(CASE WHEN MONTH(s.date) = 11 THEN s.total END),2) AS November,
ROUND(SUM(CASE WHEN MONTH(s.date) = 12 THEN s.total END),2) AS December
FROM cte
LEFT JOIN sales s ON s.date = cte.date
GROUP BY DAY(cte.date)
ORDER BY DAY(cte.date)
Demo on dbfiddle
generate your months using union and do right join
select t1.d as Days
, sum(iif(month(date) = 12, total, null) as December
from sales
right join (select 1 as d
union select 2 union select 3 union select 4 union select 5 union select 6
union select 7 union select 8 union select 9 union select 10 union select 11
.... ) as t1 on t1.d = day(date)
where year(date) = 2012
group by date
if you are using mysql v8.0, you can use recursive queries.
with recursive cte as(
select 1 as d
union all
select d + 1 from cte where d < day(last_day('2019-12-01'))
)
select coalesce(day(s.date), t1.d) as Days
, sum(iif(month(s.date) = 12, total, null) as December
from sales s
right join cte as t1 on t1.d = day(s.date)
where year(date) = 2012
group by coalesce(day(s.date), t1.d)

get total amount of expenses per month in MySQL

I am currently working with 2 tables, expenses and income. To keep the structure simple and can see it, this is the fiddle: http://sqlfiddle.com/#!9/256cd64/2
The result I need from my query is the total amount for each month of the current year, for this and tried something like this:
select sum(e.amount) as expense, DATE_FORMAT(e.date,'%m') as month
from expenses e
where year(e.date) = 2019
group by month
My problem with this is that it only takes me the months where there was registration and I would like it to take 12 months whether or not they have a registration, in the case that they did not return 0 as a total amount.
At the moment I am working with the table of expenses but I would like to have a single query that returns the monthly expenses and income, this is an example of the final output that I would like to obtain:
| Month | Expense| Incomes |
|---------|--------|---------|
| 01| 0 | 0 |
| 02| 3000 | 4000 |
| 03| 1500 | 5430 |
| 04| 2430 | 2000 |
| 05| 2430 | 1000 |
| 06| 2430 | 1340 |
| 07| 0 | 5500 |
| 08| 2430 | 2000 |
| 09| 1230 | 2000 |
| 10| 8730 | 2000 |
| 11| 2430 | 2000 |
| 12| 6540 | 2000 |
You need to generate the month values and then use left join to match to expenses:
select coalesce(sum(e.amount), 0) as expense, m.month
from (select '01' as month union all
select '02' as month union all
select '03' as month union all
select '04' as month union all
select '05' as month union all
select '06' as month union all
select '07' as month union all
select '08' as month union all
select '09' as month union all
select '10' as month union all
select '11' as month union all
select '12' as month
) m left join
expenses e
on year(e.date) = 2019 and
DATE_FORMAT(e.date,'%m') = m.month
group by m.month;
Here is a db<>fiddle.
As for income, you should ask another question about that.
You can use MONTH to get month value from your date column and then GROUP BY them to get your desired output as below-
SELECT SUM(e.amount) AS expense,
MONTH(e.date) AS month
FROM expenses e
WHERE YEAR(e.date) = 2019
GROUP BY MONTH(e.date)
Try changing your sum(e.amount) as expense to: COALESCE(sum(e.amount),0) as expense
The COALESCE function returns the first non NULL value.
SELECT
t1.month,
COALESCE(t2.amount, 0) AS expenses,
COALESCE(t3.amount, 0) AS incomes
FROM
(
SELECT 1 AS month UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9 UNION ALL
SELECT 10 UNION ALL
SELECT 11 UNION ALL
SELECT 12
) t1
LEFT JOIN
(
SELECT MONTH(date) AS month, SUM(amount) AS amount
FROM expenses
GROUP BY MONTH(date)
) t2
ON t1.month = t2.month
LEFT JOIN
(
SELECT MONTH(date) AS month, SUM(amount) AS amount
FROM incomes
GROUP BY MONTH(date)
) t3
ON t1.month = t3.month
ORDER BY
t1.month;

I need two different column values based on different condition from same table

Heading
i want to get result sum of 1st record of each 'tank id' per day wise and sum of 'receipt' per day wise and whole data will be limited for particular month
SELECT
DATE(t.DATE) AS 'Date',
SUM(t.in_stock) AS 'Opening Stock',
SUM(receipt) AS 'Receipt',
SUM(in_stock + receipt)AS 'Total Stock'
FROM diesel_tank_details t
WHERE NOT EXISTS (
SELECT
1
FROM diesel_tank_details t2
WHERE
DATE(t2.DATE) = DATE(t.DATE)
AND t2.tank_id = t.tank_id
AND t2.dt_id < t.dt_id
)
AND YEAR(DATE) = 2019
AND MONTH(DATE) = 7
GROUP BY DATE(DATE)
Tank_ID In_Stock Receipt Date
1 1000 1000 2019-07-10 00:28:33
2 2000 2000 2019-07-10 00:28:40
3 3000 3000 2019-07-10 00:28:47
1 300 0 2019-07-10 00:32:40
1 250 0 2019-07-15 15:15:06
3 2500 0 2019-07-15 15:15:37
2 1800 0 2019-07-15 15:35:49
3 2200 0 2019-07-15 16:13:17
1 500 250 2019-07-15 16:13:51
2 2000 200 2019-07-15 16:13:57
3 3000 800 2019-07-15 16:14:03
i need this Output from above table
Date Opening Stock Receipt Total Stock
2019-07-10 6000 6000 12000
2019-07-15 4550 1250 4550
Since you want different conditions for stock and receipt you must sum different rows for each case:
select s.day, s.`Opening Stock`, r.Receipt,
(s.`Opening Stock` + s.Receipt) `Total Stock`
from (
select g.day, sum(t.in_stock) `Opening Stock`, sum(t.receipt) receipt
from diesel_tank_details t inner join (
select date(date) day, tank_id, min(date) mindate
from diesel_tank_details
where in_stock <> 0
group by date(date), tank_id
) g on g.tank_id = t.tank_id and g.mindate = t.date
group by g.day
) s inner join (
select g.day, sum(t.receipt) Receipt
from diesel_tank_details t inner join (
select date(date) day, tank_id, min(date) mindate
from diesel_tank_details
where receipt <> 0
group by date(date), tank_id
) g on g.tank_id = t.tank_id and g.mindate = t.date
group by g.day
) r on r.day = s.day
See the demo.
Results:
| day | Opening Stock | Receipt | Total Stock |
| ---------- | ------------- | ------- | ----------- |
| 2019-07-10 | 6000 | 6000 | 12000 |
| 2019-07-15 | 4550 | 1250 | 4550 |
Query:
SELECT DATE(t.Date) as 'Date',
SUM(t.In_Stock) as 'Opening Stock',
SUM(t.Receipt) as 'Receipt',
SUM(t.In_Stock + t.Receipt) as 'Total Stock'
FROM diesel_tank_details t
WHERE AND YEAR(t.Date) = 2019
AND MONTH(t.Date) = 7
GROUP BY DATE(t.Date)
Just include your additional filter. I don't know what DT_ID does so i just removed it. but basically you just wanted to group it by day.

Get last row for given dates if date doesn't exist

How can we query a set of records to get data for particular dates where there might be gaps in dates.
Example Data
date | Price
------------------
2018-03-31 | 115
2018-03-29 | 114
2018-03-28 | 113
...
2017-03-29 | 117
2017-03-28 | 118
...
2016-12-30 | 143
2016-12-29 | 140
...
2015-12-31 | 110
2015-12-30 | 111
Required Data for dates: 2018-03-31, 2017-03-31, 2016-12-31, 2015-12-31
date | Price
------------------
2018-03-31 | 115
2017-03-31 | 117
2016-12-31 | 143
2015-12-31 | 110
You can do this with correlated sub query. The following will return the price for the exact date or the closest prior date:
SELECT dates.dt, (
SELECT price
FROM t
WHERE date <= dates.dt
ORDER BY date DESC
LIMIT 1
) AS price
FROM (
SELECT '2018-03-31' AS dt UNION ALL
SELECT '2017-03-31' UNION ALL
SELECT '2016-12-31' UNION ALL
SELECT '2015-12-31'
) AS dates
Demo on db<>fiddle
You can use MySQL Last day function and date_format to achieve the desired result. Check out the query :-
select last_day(a11.d_date), a11.price
from test a11
join
(select MAX(d_date) d_date, DATE_FORMAT(d_date, "%M %Y")
from test
group by DATE_FORMAT(d_date, "%M %Y")
) a12
on a11.d_date = a12.d_date
SQL Fiddle
seem you need the max price for year
select max(date), max(price)
from my_table m
inner join (
select max(date), year(date) my_year
from my_table
group by year(date)
) t on t.my_year = year(m.date)
group by year(m.date)

SUM DISTINCT MYSQL | WHERE CLAUSE

I would like to get results based on SUM from table (history), where username contains 'red' and grouped by month. here the query :
select month(date),
SUM(CASE WHEN status='success' THEN 1 ELSE 0 END) as total_sucess,
SUM(CASE WHEN status='failed' THEN 1 ELSE 0 END) as total_failed
from history
where date between '201305%' AND '201311%' AND username like '%#red%'
GROUP BY month(history.date);
the results :
+------------+--------------+--------------+
| month(date) | total_sucess | total_failed |
+------------+--------------+--------------+
| 5 | 10960 | 3573 |
| 6 | 2336 | 1202 |
| 7 | 2211 | 1830 |
| 8 | 5312 | 3125 |
| 9 | 9844 | 5407 |
| 10 | 6351 | 3972 |
+------------+--------------+--------------+
the question is , how do I get distinct total_success and total_failed SUM? just in one query ?
I've tried using this
select month(tgl),
SUM(CASE WHEN status='success' THEN 1 ELSE 0 END) as total_sucess,
SUM(DISTINCT (username) CASE WHEN status='success' THEN 1 ELSE 0 END) as distinct_total_sucess,
SUM(CASE WHEN status='failed' THEN 1 ELSE 0 END) as total_failed,
SUM(DISTINCT (username) CASE WHEN status='failed' THEN 1 ELSE 0 END) as distinct_failed_sucess
from history_auth
where tgl between '201305%' AND '201311%' AND username like '%#t.sel%'
GROUP BY month(history_auth.tgl);
but get error sql syntax... i have no idea with this :(
Best I can make out of your requirement is that you want the number of distinct usernames each month that succeeded / failed.
If so I think you need a pair of sub selects to get those figures.
Rejigged the query (adding another sub select to get the 6 months, rather than relying on all months being represented.
SELECT Sub1.aMonth,
SUM(CASE WHEN history.status='success' THEN 1 ELSE 0 END) as total_sucess,
SUM(CASE WHEN history.status='failed' THEN 1 ELSE 0 END) as total_failed,
IFNULL(SuccessCount, 0),
IFNULL(FailedCount, 0)
FROM
(
SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 0 MONTH)) AS aMonth
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 1 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 2 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 3 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 4 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 5 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 6 MONTH))
) Sub1
LEFT OUTER JOIN history
ON MONTH(history.date) = Sub1.aMonth
AND username LIKE '%#red%'
LEFT OUTER JOIN
(
SELECT MONTH(date) AS aMonth, COUNT(DISTINCT username) AS SuccessCount
FROM history
WHERE status='success'
AND username LIKE '%#red%'
GROUP BY MONTH(date)
) Sub2
ON Sub1.aMonth = Sub2.aMonth
LEFT OUTER JOIN
(
SELECT MONTH(date) AS aMonth, COUNT(DISTINCT username) AS FailedCount
FROM history
WHERE status='failed'
AND username LIKE '%#red%'
GROUP BY MONTH(date)
) Sub3
ON Sub1.aMonth = Sub3.aMonth
GROUP BY Sub1.aMonth, SuccessCount, FailedCount