Query to calculate profits in a month - sql

I have to tables:
Incomes:
| date | incomeAmount |
Expenses:
| date | expenseAmount |
I would like to sum all my incomes in an especific month using: MONTH(date) = ?
I also need to sum all my expenses in this same month
Then subtract to obtain my profit in the month, with an output table similar to that:
| Month | TotalIncomes | TotalExpenses | Profit |
How can I create a SQL Query in order to do this?
Thank you for all the help!

You can join the aggregated tables.
SELECT A.Month, TotalIncomes, TotalExpenses, TotalIncomes-TotalExpenses AS Profit
FROM (MONTH(Date) AS Month, SUM(incomeAmount) AS TotalIncomes
FROM Incomes
GROUP BY MONTH(Date)
) A
INNER JOIN (
MONTH(Date) AS Month, SUM(expenseAmount) AS TotalExpenses
FROM Incomes
GROUP BY MONTH(Date)
) B
ON A.Month = B.Month

SELECT I.Month AS Month,
I.TotalIncomes AS TotalIncomes,
E.TotalExpenses AS TotalExpenses,
I.TotalIncomes - E.TotalExpenses AS Profit
FROM ( SELECT EXTRACT(MONTH FROM DATE ) AS Month,
SUM( I.incomeAmount ) AS TotalIncomes
FROM Incomes
) I,
(
SELECT EXTRACT(MONTH FROM DATE ) AS Month,
SUM( E.expenseAmount ) AS TotalExpenses
FROM Expenses
) E
WHERE I.Month = E.Month
;

Related

Sales amounts of the top n selling vendors by month in bigquery

i have a table in bigquery like this (260000 rows):
vendor date item_price
x 2021-07-08 23:41:10 451,5
y 2021-06-14 10:22:10 41,7
z 2020-01-03 13:41:12 74
s 2020-04-12 01:14:58 88
....
exactly what I want is to group this data by month and find the sum of the sales of only the top 20 vendors in that month. Expected output:
month sum_of_only_top20_vendor's_sales
2020-01 7857
2020-02 9685
2020-03 3574
2020-04 7421
.....
Consider below approach
select month, sum(sale) as sum_of_only_top20_vendor_sales
from (
select vendor,
format_datetime('%Y%m', date) month,
sum(item_price) as sale
from your_table
group by vendor, month
qualify row_number() over(partition by month order by sale desc) <= 20
)
group by month
Another solution that potentially can show much much better performance on really big data:
select month,
(select sum(sum) from t.top_20_vendors) as sum_of_only_top20_vendor_sales
from (
select
format_datetime('%Y%m', date) month,
approx_top_sum(vendor, item_price, 20) top_20_vendors
from your_table
group by month
) t
or with a little refactoring
select month, sum(sum) as sum_of_only_top20_vendor_sales
from (
select
format_datetime('%Y%m', date) month,
approx_top_sum(vendor, item_price, 20) top_20_vendors
from your_table
group by month
) t, t.top_20_vendors
group by month

sql get balance at end of year

I have a transactions table for a single year with the amount indicating the debit transaction if the value is negative or credit transaction values are positive.
Now in a given month if the number of debit records is less than 3 or if the sum of debits for a month is less than 100 then I want to charge a fee of 5.
I want to build and sql query for this in postgre:
select sum(amount), count(1), date_part('month', date) as month from transactions where amount < 0 group by month;
I am able get records per month level, I am stuck on how to proceed further and get the result.
You can start by generating the series of month with generate_series(). Then join that with an aggregate query on transactions, and finally implement the business logic in the outer query:
select sum(t.balance)
- 5 * count(*) filter(where coalesce(t.cnt, 0) < 3 or coalesce(t.debit, 0) < 100) as balance
from generate_series(date '2020-01-01', date '2020-12-01', '1 month') as d(dt)
left join (
select date_trunc('month', date) as dt, count(*) cnt, sum(amount) as balance,
sum(-amount) filter(where amount < 0) as debit
from transactions t
group by date_trunc('month', date)
) t on t.dt = d.dt
Demo on DB Fiddle:
| balance |
| ------: |
| 2746 |
How about this approach?
SELECT
SUM(
CASE
WHEN usage.amount_s > 100
OR usage.event_c > 3
THEN 0
ELSE 5
END
) AS YEAR_FEE
FROM (SELECT 1 AS month 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 UNION
SELECT 12
) months
LEFT OUTER JOIN
(
SELECT
sum(amount) AS amount_s,
count(1) event_c,
date_part('month', date) AS month
FROM transactions
WHERE amount < 0
GROUP BY month
) usage ON months.month = usage.month;
First you must use a resultset that returns all the months (1-12) and join it with a LEFT join to your table.
Then aggregate to get the the sum of each month's amount and with conditional aggregation subtract 5 from the months that meet your conditions.
Finally use SUM() window function to sum the result of each month:
SELECT DISTINCT SUM(
COALESCE(SUM(t.Amount), 0) -
CASE
WHEN SUM((t.Amount < 0)::int) < 3
OR SUM(CASE WHEN t.Amount < 0 THEN -t.Amount ELSE 0 END) < 100 THEN 5
ELSE 0
END
) OVER () total
FROM generate_series(1, 12, 1) m(month) LEFT JOIN transactions t
ON m.month = date_part('month', t.date) AND date_part('year', t.date) = 2020
GROUP BY m.month
See the demo.
Results:
> | total |
> | ----: |
> | 2746 |
I think you can use the hanving clause.
Select ( sum(a.total) - (12- count(b.cnt ))*5 ) as result From
(Select sum(amount) as total , 'A' as name from transactions ) as a left join
(Select count(amount) as cnt , 'A' as name
From transactions
where amount <0
group by month(date)
having not(count(amount) <3 or sum(amount) >-100) ) as b
on a.name = b.name
select
sum(amount) - 5*(12-(
select count(*)
from(select month, count(amount),sum(amount)
from transactions
where amount<0
group by month
having Count(amount)>=3 And Sum(amount)<=-100))) as balance
from transactions ;

Return a single set of results from two SQL tables

I have two SQL queries that both return the same columns.
The returned column names are month_name, month_number and total.
How can I return a single set of results from the two tables, summing the "total" field?
Query 1
SELECT DISTINCT
DATENAME(MONTH,DATEADD(MONTH,month([date]),-1 )) as month_name,
MONTH([date]) as month_number,
SUM(pur.total_expenses - pur.vat) as 'total'
FROM (select distinct ID, MONTH([date]) as month_number, DATENAME(MONTH,DATEADD(MONTH,month([date]),-1 )) as month_name from [dbo].[purchase_invoices] WHERE YEAR([date]) = '2020' ) m
LEFT JOIN [dbo].[purchase_invoices] pur ON pur.id = m.id
LEFT JOIN [dbo].[dividends] div on month(dividend_date) = month([date]) AND month(dividend_date) = '2020'
WHERE YEAR([date]) = '2020'
GROUP BY m.month_number, MONTH([date]), m.month_name
ORDER BY month_number
Query 2
SELECT DISTINCT
DATENAME(MONTH,DATEADD(MONTH,month([dividend_date]),-1 )) as month_name,
MONTH([dividend_date]) as month_number,
SUM(div.dividend_value) as 'total'
FROM (select distinct ID, MONTH([dividend_date]) as month_number, DATENAME(MONTH,DATEADD(MONTH,month([dividend_date]),-1 )) as month_name from [dbo].[dividends] WHERE YEAR([dividend_date]) = '2020' ) m
LEFT JOIN [dbo].[dividends] div ON div.id = m.id
WHERE YEAR([dividend_date]) = '2020'
GROUP BY m.month_number, MONTH([dividend_date]), m.month_name
ORDER BY month_number
I know I need a JOIN on the month_name or number field, but I am not sure on how to achieve that.
Any help greatly appreciated.
UPDATE (Expected Output)
|---------------------|------------------|------------------|
| month_name | month_number | total |
|---------------------|------------------|------------------|
| Jan | 1 | 4500 |
|---------------------|------------------|------------------|
| Feb | 2 | 6000 |
|---------------------|------------------|------------------|
| ... | ... | ... |
Try using CTEs:
WITH invoice AS (
SELECT DISTINCT
ID,
MONTH([date]) AS month_number,
DATENAME(MONTH, DATEADD(MONTH, MONTH([date]), -1)) AS month_name
FROM[dbo].[purchase_invoices]
WHERE
YEAR([date]) = '2020'
),
q1 AS (
SELECT DISTINCT
DATENAME(MONTH, DATEADD(MONTH, MONTH([date]), -1)) AS month_name,
MONTH([date]) AS month_number,
SUM(pur.total_expenses - pur.vat) AS 'total'
FROM invoice as m
LEFT JOIN[dbo].[purchase_invoices] pur
ON pur.id = m.id
LEFT JOIN[dbo].[dividends] DIV
ON MONTH(dividend_date) = MONTH([date])
AND MONTH(dividend_date) = '2020'
WHERE
YEAR([date]) = '2020'
GROUP BY
m.month_number,
MONTH([date]),
m.month_name
ORDER BY
month_number
),
dividend AS (
SELECT DISTINCT
ID,
MONTH([dividend_date]) AS month_number,
DATENAME(MONTH, DATEADD(MONTH, MONTH([dividend_date]), -1)) AS month_name
FROM[dbo].[dividends]
WHERE
YEAR([dividend_date]) = '2020'
),
q2 AS (
SELECT DISTINCT
DATENAME(MONTH, DATEADD(MONTH, MONTH([dividend_date]), -1)) AS month_name,
MONTH([dividend_date]) AS month_number,
SUM(DIV.dividend_value) AS 'total'
FROM dividend as m
LEFT JOIN[dbo].[dividends] DIV
ON DIV.id = m.id
WHERE
YEAR([dividend_date]) = '2020'
GROUP BY
m.month_number,
MONTH([dividend_date]),
m.month_name
ORDER BY
month_number
)
SELECT
month_name,
month_number,
q1.total + q2.total AS total
FROM q1
LEFT JOIN q2
ON q1.month_name = q2.month_name
AND q1.month_number = q2.month_number
You can use union all:
select month_name, month_number, sum(total)
from ((<query1 here>) union all
(<query2 here>)
) q
group by month_name, month_number;

Query two unbalanced tables

Sum across two tables returns unwanted Sum from one table multiplied by the number of rows in the other
I have 1 table with Actual results recorded by date and the other tables contains planned results recorded by month.
Table 1(Actual)
Date Location Amount
01/01/2019 Loc1 1000
01/02/2019 Loc1 700
01/01/2019 Loc2 7500
01/02/2019 Loc2 1000
02/01/2019 Loc1 500
Table 2(Plan)
Year Month Location Amount
2019 1 Loc1 1500
2019 1 Loc2 8000
2019 2 Loc1 800
I have tried various differed Joins using YEAR(Table1.date) and Month(table1.date) and grouping by
Month(Table1.Date) but I keep running into the same problem where the PlanAmount is multiplied by however many rows in the Actual table...
in the example of loc1 for Month 1 below I get
Year Month Location PlanAmount ActualAmount
2019 1 Loc1 3000 1700
I would like to return the below
Year Month Location PlanAmount ActualAmount
2019 1 Loc1 1500 1700
2019 1 Loc2 8000 8500
2019 2 Loc1 800 500
Thanks in advance for any help
D
You can do this with a full join or union all/group by:
select yyyy, mm, location,
sum(actual_amount) as actual_amount,
sum(plan_amount) as plan_amount
from ((select year(date) as yyyy, month(date) as mm, location,
amount as actual_amount, 0 as plan_amount
from actual
group by year(date) as yyyy, month(date) as mm, location
) union all
(select year, month, location,
0 as actual_amount, amount as plan_amount
from actual
group by year, month, location
)
) ap
group by yyyy, mm, location;
This ensures that you have rows, even when there are no matches in the other table.
To get the required results you need to group the first table on year of date, month of date and location and need to select the columns year, month, location and sum of amount from group after that you need to join that resultant r
SELECT
plans.year,
plans.month,
plans.location,
plans.plan_amount,
grouped_results.actual_amount
FROM plans
INNER JOIN (
SELECT
datepart(year, date) AS year,
datepart(month, date) AS month,
location,
SUM(amount) AS actual_amount
FROM actuals
GROUP BY datepart(year, date), datepart(month, date), location
) as grouped_results
ON
grouped_results.year = plans.year AND
grouped_results.month = plans.month AND
grouped_results.location = plans.location
I think the problem is that you are using sum(PlanTable.Amount) when grouping. Try using max(PlanTable.Amount) instead.
select
p.Year,
p.Month,
p.Location,
sum(a.Amount) as actual_amount,
max(p.Amount) as plan_amount
from
[Plan] p left join Actual a
on year(a.date) = p.year
and month(a.date) = p.Month
and a.Location = p.Location
group by
p.year,
p.month,
p.Location
SQL Fiddle
get year and month from date and use them in join , most dbms has year and month functions you can use according to your DBMS
select year(t1.date) yr,month(t1.date) as monthofyr ,t1.Location,
sum(t1.amount) as actual_amoun,
sum(t2.amount) as planamount
from table1 t1 left join table2 t2 on
month(t1.date)= t2.Month and t1.Location=t2.Location
and year(t1.date)=t2.year
group by year(t1.date) ,month(t1.date),Location

Grouping data with step down summation

I have a table with OrderDate,TotalAmount. I want to display month and TotalAmount of month with total amount of previous month to be added in next month.
e.g.
OrderDate TotalAmount
---------- -----------
13.01.1998--- 10
15.01.1998--- 11
01.02.1998--- 12
18.02.1998--- 10
12.03.1998--- 09
Output should be
Month TotalSum
------ --------
1--- 21
2--- 43
3--- 52
If your data would only be from a single calendar year, you could use
with g as
( select month(orderdate) as ordermonth,
sum( totalamount ) as sales
from orders
group by month(orderdate)
)
select m.ordermonth, sum(t.sales) as totalsales
from g as m
join g as t on m.ordermonth >= t.ordermonth
group by m.ordermonth
order by m.ordermonth
But if there is ANY chance that your data could have two years, then you need year in there as well, so construct your month to include year.
with g as
( select format(orderdate, 'yyyy-MM') as ordermonth,
sum( totalamount ) as sales
from orders
group by format(orderdate, 'yyyy-MM')
)
select m.ordermonth, sum(t.sales) as totalsales
from g as m
join g as t on m.ordermonth >= t.ordermonth
group by m.ordermonth
order by m.ordermonth