Grouping data with step down summation - sql

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

Related

Year over Year by Month Comparison and Month to Date in BigQuery

Edit: #shawnt00 has the correct answer. Thank you very much!
I am having trouble accurately doing a year over year comparison by month but at any point during the month. For example for August 2022 vs 2021, I want to compare August 1 - August 25, rather than full month of August 2021.
I am also using a daily date field.
I want the final result to basically be:
Product_ID, Year, Month, PY_Sales, CY_Sales
Edit: I have daily totals. Some products do have not sales on certain days though:
product_id
sale_date
units
1
2021-01-01
5
2
2021-01-02
4
...
...
...
1
2021-06-05
2
2
2022-01-06
1
2
2022-08-15
9
This is the code I have, but it doesn't do MTD. So 2021 August is the entire month of August and I want it the same dates for 2022. I used this code because some products do not have sales on certain months.
WITH cte AS
(
SELECT
PRODUCT_ID,
EXTRACT(YEAR FROM SALE_DATE) AS Year,
EXTRACT(MONTH FROM SALE_DATE) AS Month,
CONCAT(EXTRACT(YEAR FROM SALE_DATE), '-',EXTRACT(MONTH FROM SALE_DATE)) AS Year_Month,
SUM(Units) AS Units
FROM data
WHERE Product_ID = 1
AND DATE(SALE_DATE) >= '2019-01-01'
GROUP BY 1, 2, 3
),
diff AS
(
SELECT
COALESCE(c.PRODUCT_ID, p.PRODUCT_ID) AS Product_ID,
COALESCE(c.Year, p.Year + 1) AS Year,
COALESCE(c.Month, p.Month) AS Month,
IFNULL(c.Units, 0) AS Current_Units,
IFNULL(p.Units, 0) AS Previous_Units,
NULLIF(((IFNULL(c.Units, 0) - IFNULL(p.Units,0)) / p.Units),0) * 100 AS Percent_Change
FROM CTE c
FULL OUTER JOIN CTE p ON c.PRODUCT_ID = p.PRODUCT_ID AND c.Year = p.Year + 1 AND c.Month = p.Month
WHERE c.Year <= EXTRACT(YEAR FROM CURRENT_DATE())
ORDER BY 2, c.Year, c.Month
)
SELECT *
FROM diff
--This is to avoid dividing by 0
WHERE diff.Previous_Units > 0
--AND Percent_Change <= -.5
You could just roll up two different monthly totals and then switch for the current month comparison:
with agg as (
select
PRODUCT_ID,
extract(year from SALE_DATE) as yr,
extract(month from SALE_DATE) as mth,
sum(Units) as Units,
sum(case when extract(day from SALE_DATE) <= extract(day from current_date())
then Units end) as UnitsMTD
from data
where date(SALE_DATE) >= '2019-01-01' -- one year before report output
group by 1, 2, 3
)
select c.Yr, c.Mth, c.PRODUCT_ID,
case when Yr = extract(year from current_date())
and Mth = extract(month from current_date())
then (c.UnitsMTD - p.UnitsMTD) / p.UnitsMTD
else (c.Units - p.Units ) / p.Units
end as Percent_Change
from agg c left outer join agg p
on p.Product_ID = c.Product_ID and p.Yr = c.Yr - 1 and p.Mth = c.Mth
order by c.Yr, c.Mth, c.PRODUCT_ID;
Note my earlier comment about leap years. This will treat February 28 of the year following a leap year as an "MTD" month. You might need to handle that differently inside the case expression.

How to query total sum of sales by month by teams?

What is the best way to get the best selling teams by each month when I have tables like these:
The results should be something like this (group by total price of orders):
Month | Team | Sales
____________________
March | 2 | 3453
April | 3 | 1353
May | 2 | 5341
I have joined two tables before but for some reason joining 4 tables and grouping them by month seems difficult.
Thank you.
In Postgres, you can use distinct on -- if you want exactly one row per month:
select date_trunc('month', created), e.Team_nr, sum(p.price) as Sales
from employee e join
orders o
on e.id = o.employee_ID join
products p
on p.id = orders.product_id
group by e.Team_nr, date_trunc('month', o.Created)
order by date_trunc('month', o.Created), sum(p.price) desc
This should do it. I added the year too. It uses a CTE
with cte as
(
select Team_nr, sum(price) as Sales, date_part('month', Created) as _Month, date_part('year', Created) as _Year
from employee e
inner join orders o on e.id = o.employee_ID
inner join products p on p.id = orders.product_id
Group by Team_nr, date_part('month', Created), date_part('year', Created)
)
select Team_nr, Sales, _Month, _Year
from cte a
where not exists(select 1 from cte b where
a._Month = b._Month and a._Year = b._Year and a.Team_nr <> b.Team_nr and a.Sales < b.Sales )

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

Query to calculate profits in a month

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
;

Sum a subquery and group by customer info

I have three tables something like the following:
Customer (CustomerID, AddressState)
Account (AccountID, CustomerID, OpenedDate)
Payment (AccountID, Amount)
The Payment table can contain multiple payments for an Account and a Customer can have multiple accounts.
What I would like to do is retrieve the total amount of all payments on a State by State and Month by Month basis. E.g.
Opened Date| State | Total
--------------------------
2009-01-01 | CA | 2,500
2009-01-01 | GA | 1,000
2009-01-01 | NY | 500
2009-02-01 | CA | 1,500
2009-02-01 | NY | 2,000
In other words, I'm trying to find out what States paid the most for each month. I'm only interested in the month of the OpenedDate but I get it as a date for processing afterwards. I was trying to retrieve all the data I needed in a single query.
I've been trying something along the lines of:
select
dateadd (month, datediff(month, 0, a.OpenedDate), 0) as 'Date',
c.AddressState as 'State',
(
select sum(x.Amount)
from (
select p.Amount
from Payment p
where p.AccountID = a.AccountID
) as x
)
from Account a
inner join Customer c on c.CustomerID = a.CustomerID
where ***
group by
dateadd(month, datediff(month, 0, a.OpenedDate), 0),
c.AddressState
The where clause includes some general stuff on the Account table. The query won't work because the a.AccountID is not included in the aggregate function.
Am I approaching this the right way? How can I retrieve the data I require in order to calculate which States' customers pay the most?
If you want the data grouped by month, you need to group by month:
SELECT AddressState, DATEPART(mm, OpenedDate), SUM(Amount)
FROM Customer c
INNER JOIN Account a ON a.CustomerID = c.CustomerID
INNER JOIN Payments p ON p.AccountID = a.AccountID
GROUP BY AddressState, DATEPART(mm, OpenedDate)
This shows you the monthnumber (1-12) and the total amount per state. Note that this example doesn't include years: all amounts of month 1 are summed regardless of year. Add a datepart(yy, OpenedDate) if you like.
In other words, I'm trying to find out what States paid the most for each month
This one will select the most profitable state for each month:
SELECT *
FROM (
SELECT yr, mon, AddressState, amt, ROW_NUMBER() OVER (PARTITION BY yr, mon, addressstate ORDER BY amt DESC) AS rn
FROM (
SELECT YEAR(OpenedDate) AS yr, MONTH(OpenedDate) AS mon, AddressState, SUM(Amount) AS amt
FROM Customer c
JOIN Account a
ON a.CustomerID = c.CustomerID
JOIN Payments p
ON p.AccountID = a.AccountID
GROUP BY
YEAR(OpenedDate), MONTH(OpenedDate), AddressState
)
) q
WHERE rn = 1
Replace the last condition with ORDER BY yr, mon, amt DESC to get the list of all states like in your resultset:
SELECT *
FROM (
SELECT yr, mon, AddressState, amt, ROW_NUMBER() OVER (PARTITION BY yr, mon, addressstate ORDER BY amt DESC) AS rn
FROM (
SELECT YEAR(OpenedDate) AS yr, MONTH(OpenedDate) AS mon, AddressState, SUM(Amount) AS amt
FROM Customer c
JOIN Account a
ON a.CustomerID = c.CustomerID
JOIN Payments p
ON p.AccountID = a.AccountID
GROUP BY
YEAR(OpenedDate), MONTH(OpenedDate), AddressState
)
) q
ORDER BY
yr, mon, amt DESC
select
AddressState,
year(OpenedDate) as Yr,
month(OpenedDate) as Mnth,
sum(Payment) as SumPayment
from Customer c
inner join Account a
on c.CustomerID=a.CustomerID
inner join Payment p
on a.AccountID=p.AccountID
group by AddressState, month(OpenedDate)