mysql select data with calculation between 2 tables and group by month - mysql

I have 2 mysql tables like bellow:
Table income Table expense
id amount date id amount date
1 200 2011-12-10 1 100 2011-12-21
2 300 2011-12-14 2 150 2012-01-01
3 500 2012-01-05 2 200 2012-01-03
I want to get data in this way:
month profit
december, 2011 400
january, 2012 150
Is this possible in a single query?

What you want is a union with some date magic for the aggregate:
select
date_format(x.date, '%M %Y') as `month`,
sum(amount) as profit
from
(select amount, date
from income
union all
select amount*-1 as amount, date
from expense
) x
group by
date_format(x.date, '%M %Y')
The specific thing we're using here is date_format to get the dates the way we want it. Also, we're multiplying amount by -1 when it comes from expense to make the calculation correct. Of course, we could have returned different columns for income and expense and then did the math on them in the sum, such as sum(x.income_amount, x.expense_amount), but there's no need to do that when you can just quickly do a *-1 to the column on before it hits the sum.
The other important thing is the group by. Here, we're grouping by a function that formats the date as Month, YYYY, so it will get the divisions you're looking for.

Related

how to find the first and last records of every month in mysql database

I want to fetch the first and last record of every month in sql but my query give the results below and here is my query
SELECT DISTINCT month, amount,
MIN(date) OVER (PARTITION BY month ORDER BY utility.month)
FROM
utility;
results of the query above
month
amount
min(date)
February/2022
200
2022-02-02
January/2022
1000
2022-01-01
January/2022
200
2022-01-01
March/2022
1000
2022-02-06
You can get the MIN() and MAX() value first, turn into a subquery then join utility table twice to get the amount corresponding to the extracted dates, like this:
SELECT v.month,
v.mindt,
u1.amount,
v.maxdt,
u2.amount
FROM
(SELECT month,
MIN(date) mindt, MAX(date) maxdt
FROM
utility
GROUP BY month) v
JOIN utility u1 ON u1.date=v.mindt
JOIN utility u2 ON u2.date=v.maxdt
;
That will give result something like this:
month
mindt
amount
maxdt
amount
January2022
2022-01-02
250
2022-01-29
350
February2022
2022-02-01
300
2022-02-28
500
March2022
2022-03-03
500
2022-03-18
300
Or you can modify the subquery to do UNION ALL, join utility once and return all in just the original 3 columns:
SELECT v.month,
v.minmaxdt,
u.amount
FROM
(SELECT month,
MIN(date) minmaxdt
FROM utility
GROUP BY month
UNION ALL
SELECT month,
MAX(date)
FROM utility
GROUP BY month
) v
JOIN utility u ON u.date=v.minmaxdt
ORDER BY v.month, v.minmaxdt;
That will give result something like this:
month
minmaxdt
amount
February2022
2022-02-01
300
February2022
2022-02-28
500
January2022
2022-01-02
250
January2022
2022-01-29
350
March2022
2022-03-03
500
March2022
2022-03-18
300
Demo fiddle
Try using MIN and MAX at the same time together with GROUP BY.
Check this from W3Schools.
The MIN() function returns the smallest value of the selected column.
The MAX() function returns the largest value of the selected column.
Try this code:
SELECT DISTINCT month, amount, MIN(date), MAX(date) FROM utility GROUP BY month;

MySQL monthly sums

I have a table transactions like this
date
amount
2020-02-26
1000
2020-02-26
1500
2021-01-11
200
I want to select the sum of all transactions per month. The result should look something like...
month
sum
2020-02
2500
2021-01
200
This is what I've got so far
select sum(amount) sum, MONTH(date) month from transactions group by month;
However this groups by month ignoring year (the values returned for month are single integers). I need to group by each individual month (return a different row for Jan 2020 vs Jan 2021)
here is one way:
select sum(amount) sum, extract(YEAR_MONTH FROM `date`) month
from transactions
group by month;

Is there a way to return non existent values when using groupby and union all?

I have a table that I'm trying to return summarized results from for a chart that will have two datasets, one for debit transactions that have happened each month and one for payments that have happened each month. The table I'm querying looks like this:
ID amount type created_date
1 200 Debit 2020-02-14
2 150 Payment 2020-02-15
3 200 Payment 2020-02-21
4 100 Debit 2020-03-01
5 100 Debit 2020-03-05
6 50 Payment 2020-04-01
7 150 Payment 2020-04-02
What I'm looking to get back out of my query is a result that does a sum(amount) once for each type and unions them together so that the result looks something like this:
month debit_sum payment_sum
February 200 350
March 200 0
April 0 200
So far I've gotten to the below query which returns mostly the right data but in the wrong format.
SELECT MONTHNAME(created_date) as month_name, type as type_name, sum(amount) as amount
FROM
(
SELECT created_date, amount, type
FROM balance_adjustments
WHERE type = 'Payment'
AND created_date > '2020-01-01'
GROUP BY MONTHNAME(created_date)
UNION ALL
SELECT created_date, amount, type
FROM balance_adjustments
WHERE type = 'Debit'
AND created_date > '2020-01-01'
GROUP BY MONTHNAME(created_date)
) AS adjustments
GROUP BY type, MONTHNAME(created_date)
ORDER BY created_date;
This returns the below, which you can see returns one row for each type, but doesn't return a row if there is no values to sum. I could work with this format if I could have it return 2 rows for each month (one for each type) and if there is no values return 0.
month type_name amount
February Debit 200
February Payment 350
March Debit 200
April Payment 200
What I can't figure out is if there is a way to either return the above with non-existent values getting filled in with 0 or if there is a way to do sum(amount) twice, once for each type as separate columns.
If I follow you correctly, you can use conditional aggregation:
select extract(year_month from created_date) created_month,
sum(case when type = 'Debit' then amount else 0 end) as debit_sum,
sum(case when type = 'Payment' then amount else 0 end) as payment_sum
from balance_adjustments
where created_at >= '2020-01-01'
group by created_month
I used year_month instead of the name of the month, since you probably want to not count the same month in different years together. You can change that to something else if you want otherwise.

Adding multiple columns' rows together

I'm trying to arrange data in a table. The table has the following columns:
Customer Name, Amount, Day. The customer names are not distinct, the amount is an amount represented by dollars and the Day is over the course of 365 days.
I'm trying to arrange the amount paid per quarter, regardless of the customer name.
This is a homework assignment and I've tried this code
SELECT day as 'Quarter', SUM(amount) as 'Total Earnings'
FROM invoices
WHERE day BETWEEN 0 and 90
GROUP BY day
I'm running into 3 problems. I did the above code just to test that it would work for one quarter before i tried to tackle the whole year.
The first problem is that I need the day 'value' to be 'First' and I'm not sure how to do that at all.
Secondly, it is totaling the amounts, but not 0-90, it's totaling 1, 2, 3... 89, 90. Rather than a single row with the total 'amounts' for days 0-90.
Lastly, I'm not sure how to do another sum for the other quarters (91-180, 181-270, 271-365). I'm assuming possibly subqueries, but I'm not sure how to do that while using WHERE/BETWEEN.
My output should be something like:
Quarter | Total Earnings
-------------------------
First | 111111111
Second | 111111111
Third | 111111111
Fourth | 111111111
SELECT 'first' AS quarter, SUM(amount) AS total_earnings
FROM invoices where day between 0 AND 90
UNION ALL
SELECT 'second' AS quarter, SUM(amount) AS total_earnings
FROM invoices where day between 91 AND 180
UNION ALL
SELECT 'third' AS quarter, SUM(amount) AS total_earnings
FROM invoices where day between 181 AND 270
UNION ALL
SELECT 'fourth' AS quarter, SUM(amount) AS total_earnings
FROM invoices where day >= 271
This will get you the expected results. The group by you were using will try to group based on day unlike on quater
You could use a CASE to find what quarter a day is in and then group by that. Something like this:
SELECT `quarter` AS 'Quarter',
SUM(amount) AS 'Total Earnings'
FROM (
SELECT CASE WHEN DAY < (365/4)
THEN 'First'
WHEN t.`day` < (365/4)*2
THEN 'Second'
WHEN t.`day` < (365/4)*3
THEN 'Third'
ELSE 'Fourth'
END AS `quarter`,
t.*
FROM `table` t
) t2
GROUP BY `quarter`;
You could of course replace the 365/ whatever with just a number of days or set a variable for the number of days in a year like SET #days_in_year = 365;. I'm just manually calculating to give a quick explanation of what the number is.
With a CASE statement you can evaluate the Quarter and then you can group by Quarter:
SELECT
case
when day BETWEEN 0 and 90 then 'First'
when day BETWEEN 91 and 180 then 'Second'
when day BETWEEN 181 and 270 then 'Third'
else 'Fourth'
end Quarter,
SUM(amount) as `Total Earnings`
FROM invoices
GROUP BY Quarter
Change the day ranges as you like.

selecting the month day and year from a transaction table

I want to create two queries for my table which has fields name,surname and amount paid,the first query should select the day,month and the amount paid,the second query should select a month,year in that year and the total amount paid in that month,lets say john paid on 2013-05-01, on 2013-05-03,while peter paid on 2013-04-08, i want the first query to output
month and day amount
05-01 200
05-03 400
04-08 50
and the second query should output:
month and year total
2013-05 600
2013-04 50
I know I can use the sum aggregate function to select the total but the tricky part is how to select the day and the month in the format above,
first query will be
SELECT DATE_FORMAT(date, "%m-%d") AS 'month and day',price as amount FROM `tablename`
and second query will be
SELECT DATE_FORMAT(date, "%Y-%m") AS 'month and year' , SUM(price) AS total FROM `tablename` GROUP BY YEAR(date), MONTH(date)