How to Get Monthly/Quarterly Sales Percent - sql

I Have a table of Monthly Sales
Quarter Month Monthly Sales
1 Jan 586
1 Feb 204
1 Mar 465
2 Apr 684
2 May 756
2 Jun 97
Now I want a result a below
Quarter Month Monthly Sales Total Sales% Quarterly Sales%
1 Jan 586 20.98% 45.25%
1 Feb 204 7.30% 16.25%
1 Mar 465 16.65% 37.05%
2 Apr 684 24.49% 44.50%
2 May 756 27.07% 49.18%
2 Jun 97 3.47 6.31%
Total Sales% = Monthly Sales/Sum (Monthly Sales)
Quarterly Sales% = Monthly Sales/ Quarterly Sales
How Do i Get this output in SQL?

Using CROSS APPLY:
SELECT
s.*,
[Total Sales %] = CAST(CAST(s.MonthlySales/t.Total * 100.00 AS DECIMAL(5, 2)) AS VARCHAR(5)) +'%',
[Quarterly Sales %] = CAST(CAST(s.MonthlySales/q.QtrTotal* 100.00 AS DECIMAL(5, 2)) AS VARCHAR(5)) +'%'
FROM Sales s
CROSS APPLY(
SELECT Total = SUM(MonthlySales) * 1.0
FROM Sales
) t
CROSS APPLY(
SELECT QtrTotal = SUM(MonthlySales) * 1.0
FROM Sales
WHERE Quarter = s.Quarter
GROUP BY Quarter
)q

If you are using Oracle you should be using the ratio_to_report analytic function. (http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions124.htm)
select quarter,
month,
monthly_sales,
round(ratio_to_report(monthly_sales) over() * 100, 2) as total_sales_pct,
round(ratio_to_report(monthly_sales) over(partition by quarter) * 100, 2) as qtr_sales_pct
from monthly_sales;
Fiddle: http://sqlfiddle.com/#!4/71aeb7/1/0
The above assumes you're selecting data for just one year as the analytic function will interpret the given result set. If your query spans multiple years you need to additionally partition by whatever column represents the year in your real table.

Related

Sum of last 12 months

I have a table with 3 columns (Year, Month, Value) like this in Sql Server :
Year
Month
Value
ValueOfLastTwelveMonths
2021
1
30
30
2021
2
24
54 (30 + 24)
2021
5
26
80 (54+26)
2021
11
12
92 (80+12)
2022
1
25
87 (SUM of values from 1 2022 TO 2 2021)
2022
2
40
103 (SUM of values from 2 2022 TO 3 2021)
2022
4
20
123 (SUM of values from 4 2022 TO 5 2021)
I need a SQL request to calculate ValueOfLastTwelveMonths.
SELECT Year,
       Month,
Value,
SUM (Value) OVER (PARTITION BY Year, Month)
FROM MyTable
This is much easier if you have a row for each month and year, and then (if needed) you can filter the NULL rows out. The reason it's easier is because then you know how many rows you need to look back at: 11.
If you make a dataset of the years and months, you can then LEFT JOIN to your data, aggregate, and then finally filter the data out:
SELECT *
INTO dbo.YourTable
FROM (VALUES(2021,1,30),
(2021,2,24),
(2021,5,26),
(2021,11,12),
(2022,1,25),
(2022,2,40),
(2022,4,20))V(Year,Month,Value);
GO
WITH YearMonth AS(
SELECT YT.Year,
V.Month
FROM (SELECT DISTINCT Year
FROM dbo.YourTable) YT
CROSS APPLY (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12))V(Month)),
RunningTotal AS(
SELECT YM.Year,
YM.Month,
YT.Value,
SUM(YT.Value) OVER (ORDER BY YM.Year, YM.Month
ROWS BETWEEN 11 PRECEDING AND CURRENT ROW) AS Last12Months
FROM YearMonth YM
LEFT JOIN dbo.YourTable YT ON YM.Year = YT.Year
AND YM.Month = YT.Month)
SELECT Year,
Month,
Value,
Last12Months
FROM RunningTotal
WHERE Value IS NOT NULL;
GO
DROP TABLE dbo.YourTable;

Cumulative sum of previous rows for each partition

I want to calculate the cumulative sum of monthly orders for each customer in my database.
For example, I have this data:
customer
year
month
no_orders
1544
2022
4
5
1544
2022
4
1
1544
2022
12
1
1544
2023
1
3
And the result should be the same as below:
customer
year
month
cumulative no_orders
1544
2022
4
0
1544
2022
12
6
1544
2023
1
7
I used lag() and in the next step, sum() over () but my result was false!
How can I solve this problem?
As #Larnu advises in the comments
Seems like you need to do several steps here. Aggregate (and group)
into months first, and then use a cumulative SUM but have the window
not include the current row.
Some SQL to implement this idea is below (DB FIDDLE)
SELECT customer,
year,
month,
cumulative_no_orders = ISNULL(SUM(SUM(no_orders))
OVER (
PARTITION BY customer
ORDER BY year, month
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
, 0)
FROM YourTable
GROUP BY customer,
year,
month
It first does the aggregation
SELECT customer,
year,
month,
sum_no_orders = SUM(no_orders)
FROM YourTable
GROUP BY customer, year, month
to return the following
customer
year
month
sum_no_orders
1544
2022
4
6
1544
2022
12
1
1544
2023
1
3
and then calculates the running total of sum_no_orders from previous rows on top of that.
Can you try this,
SELECT
customer
, year
, month
, ISNULL(LAG(no_orders) OVER (PARTITION BY customer ORDER BY customer, year, month),0) Cum_Orders
FROM (
SELECT
DISTINCT customer, year, month
, SUM(no_orders) OVER (PARTITION BY customer ORDER BY customer, year, month) no_orders
FROM ABC
) a

SQL query to compare budget to actual expenses

I'm struggling to build a query comparing budget to actual expense items.
The budget table has a single record per month/category on the first day of the month whereas the expense table has many records throughout the days of the month.
My desired result:
YEAR
MONTH
category
budgetAmt
sumExpenseAmt
2021
1
daily
100
49
2021
1
monthly
42
87
2021
2
daily
101
36
2021
2
monthly
55
82
What I'm getting:
YEAR
MONTH
category
budgetAmt
sumExpenseAmt
2021
1
daily
100
85
2021
1
monthly
42
169
2021
2
daily
101
85
2021
2
monthly
55
169
The amounts in "sumExpenseAmt" are wrong AND they're repeating.
(85 is the sum of all expense-daily items (jan + feb): 40 + 9 + 32 + 4)
(169 is the sum of all expense-monthly items (jan + feb): 83 + 4 + 75 +7)
MY SQL:
SELECT YEAR( "b"."date" ) AS "Year"
, MONTH( "b"."date" ) AS "Month"
, "b"."category"
, "b"."budgetAmt"
, SUM( "e"."expenseAmt" ) AS "sumExpenseAmt"
FROM "budget" AS "b"
JOIN "expense" AS "e" ON "b"."category" = "e"."category"
GROUP BY YEAR( "b"."date" ), MONTH( "b"."date" ), "b"."category", "b"."budgetAmt"
table: budget
date
category
budgetAmt
2021-01-01
daily
100
2021-01-01
monthly
42
2021-02-01
daily
101
2021-02-01
monthly
55
table: expense
date
category
expenseAmt
2021-01-04
daily
40
2021-01-07
daily
9
2021-01-08
monthly
83
2021-01-25
monthly
4
2021-02-01
daily
32
2021-02-05
daily
4
2021-02-15
monthly
75
2021-02-20
monthly
7
I've tried aggregating the expense table with a query and feeding the result into my initial SQL query, but that gives me the same result.
query: qry_summary_expense
date
category
budgetAmt
2021-01-01
daily
49
2021-01-01
monthly
87
2021-02-01
daily
36
2021-02-01
monthly
82
SELECT YEAR( "b"."date" ) AS "Year"
, MONTH( "b"."date" ) AS "Month"
, "b"."category", "b"."budgetAmt"
, SUM( "e"."expenseAmt" ) AS "sumExpenseAmt"
FROM "budget" AS "b"
JOIN "qry_summary_expense" AS "e" ON "b"."category" = "e"."category"
GROUP BY YEAR( "b"."date" ), MONTH( "b"."date" ), "b"."category", "b"."budgetAmt"
I'd join on both month and category
select year(b.date) as year,
month(b.date) as month,
b.category,
avg(budgetAmt) as budgetAmt,
sum(expenseAmt) as expenseAmt
from expense e
join budget b
on (month(b.date) = month(e.date)
and b.category = e.category)
group by year(b.date), month(b.date), b.category
You need to join the two tables on category AND month (using eomonth (end of month) does the trick).
SELECT Year(Eomonth(e.date)) AS year,
Month(Eomonth(e.date)) AS month,
e.category,
Avg(budgetamt) AS budgetAmt,
Sum(expenseamt) AS sumExpenseAmt
FROM expense e
INNER JOIN budget b
ON Eomonth(e.date) = Eomonth(b.date)
AND e.category = b.category
GROUP BY e.category,
Eomonth(e.date);
Fiddle
Alternatively you can use left or right joins based on your need.
select b.Year,b.Month,b.category,Budget, Expenses
from(
Select year(date) [Year] ,month(date) [Month] ,category,sum(budgetAmt) Budget
from budget
group by year(date),month(date),category
) b
Join
(
Select year(date) [Year] ,month(date) [Month] ,category,sum(expenseAmt) Expenses
from expense
group by year(date),month(date),category
) e
on b.Month = e.Month and b.Year = e.Year and b.category = e.category
Another Approach Using EOMONTH Function:
select Year(a.dates) Year ,month(a.dates) Month,a.category,sum(Budget) Budget, sum(Expenses) Expenses
from(
Select EOMONTH(date) dates,category,sum(budgetAmt) Budget
from #budget
group by EOMONTH(date),category
) a
Join
(
Select EOMONTH(date) dates,category,sum(expenseAmt) Expenses
from #expense
group by EOMONTH(date),category
) b
on a.dates = b.dates and a.category = b.category
group by Year(a.dates),month(a.dates),a.category

check whether values lies between or not in SQL

These are my 2 tables
table 2:
date_val
yr_num
yr_wk_num
day_wk_num
yr_wk_nm
day
mo_num
20200808
2020
32
6
202032
Saturday
08
20200809
2020
32
7
202032
Sunday
08
20200810
2020
33
1
202033
Monday
08
20200811
2020
33
2
202033
Tuesday
08
20200812
2020
33
3
202033
Wednesday
08
table1:
sku_id
dateval
sales
ab124
20210603
10
ab124
20210502
20
ab123
20210606
30
Need to check sales is with in + or - 30% of 2 month avg sales
with CTE
as
(
select * from table1 where dateval >= dateadd(mm, -2, dateval)
)
select dateval, sum(sales) as [Total Sales], avg(sales) as [Average Sales] from CTE group by dateval order by 1
I tried below also...
with CTE
as
(
select * from table1 t1 left join table2 t2 on t1.dateval = t2.date_val where t2.date_val >= dateadd(mm, -2, t1.dateval)
)
select dateval,sum(sales) as [Total Sales], avg(sales) as [Average Sales] from CTE group by dateval order by 1
here am doing filtering within table1 but i need to use table 2 to get filtered for past two months and get avg sales.
Next, i need to do +30% to that result avg and -30% result avg and check whether my sales is withn avg sales( avg30% above or below) or not if yes '1' if not '0'
For Ex:
Historic 2 month avg sales 100.
(+30% of avg sales is 130)
(-30% of avg sales is 70)
if sales is 120. i need to check 120 lies between 70 to 130.
please suggest me how to achieve

Show formula result applying on more than one column

I am on Microsoft SQL Server 2017.
I am having these results on SQL Server applying a simple SELECT sentence:
SELECT Result, Year, Month FROM Table;
Result Year Month
DELAY 2019 5
DELAY 2019 1
PUNCTUAL 2020 2
PUNCTUAL 2020 2
PUNCTUAL 2020 3
PUNCTUAL 2020 3
PUNCTUAL 2020 3
PUNCTUAL 2020 3
DELAY 2020 3
PUNCTUAL 2020 3
I need to get the percentage of PUNCTUAL results, separated by month and year columns. The formula would be the sum of total results separated by month and year, divided by the PUNCTUAL results multiplied * 100.
For instance, on March 2020 would be: 5 punctual / 6 results * 100 = 83.3% punctual results; the other rest are delays, which I am not interested in.
I tried with COUNT CASE WHEN but I couldn't get it to work properly.
For instance, the result I need to get would be like:
Year Month Success
2019 1 0%
2019 5 0%
2020 2 100%
2020 3 83.3%
Thanks for your help.
I like using AVG() for this:
select year, month,
avg(case when result = 'PUNCTUAL' then 1.0 else 0 end) as punctual_rate
from t
group by year, month
order by year, month;
If you want a number between 0 and 100, just use 100.0 rather than 1.0.
You may use the following statement to calculate the percentage using COUNT() and CASE:
SELECT
Year,
Month,
COUNT(CASE WHEN Result = 'PUNCTUAL' THEN 1 END) * 100.0 / COUNT(*) As Success
FROM (VALUES
('DELAY', 2019, 5),
('DELAY', 2019, 1),
('PUNCTUAL', 2020, 2),
('PUNCTUAL', 2020, 2),
('PUNCTUAL', 2020, 3),
('PUNCTUAL', 2020, 3),
('PUNCTUAL', 2020, 3),
('PUNCTUAL', 2020, 3),
('DELAY', 2020, 3),
('PUNCTUAL', 2020, 3)
) v (Result, Year, Month)
GROUP BY Year, Month
ORDER BY Year, Month
Result:
Year Month Success
2019 1 0.000000000000
2019 5 0.000000000000
2020 2 100.000000000000
2020 3 83.333333333333
If you need to get the percentage as text, you may use CONCAT():
CONCAT(
CONVERT(numeric(10, 2), COUNT(CASE WHEN Result = 'PUNCTUAL' THEN 1 END) * 100.0 / COUNT(*)),
'%') AS SuccessFormatted
You can use the aggregation with the FORMAT command to get your desired result:
CREATE TABLE #T1(Result varchar(10),Year int, Month int)
INSERT INTO #T1 VALUES('DELAY', 2019, 5),
('DELAY',2019,1),
('PUNCTUAL',2020,2),
('PUNCTUAL',2020,2),
('PUNCTUAL',2020,3),
('PUNCTUAL',2020,3),
('PUNCTUAL',2020,3),
('PUNCTUAL',2020,3),
('DELAY',2020,3),
('PUNCTUAL',2020,3)
SELECT [Year], [Month], FORMAT( (((PuncCnt * 1.0) / Total)) ,'P2') Success
FROM
(
SELECT [Year],[Month], SUM(CASE WHEN Result = 'PUNCTUAL' THEN 1 ELSE 0 END) PuncCnt, COUNT(*) Total
FROM #t1
GROUP BY [Year],[Month]
) T2
ORDER BY [Year],[Month]
RESULT:
Year Month Success
2019 1 0.00%
2019 5 0.00%
2020 2 100.00%
2020 3 83.33%