subtract two rows which are result of group bc clause - sql

I am having a case where I have raw data I am grouping it according to year and then I have to subtract the year to get the value but it is not working. example data is
year, profit,gross
2020 252563.02E9 352563.02E9
2022 102563.02E9 352563.02E9
2021 352563.02E9 352563.02E9
2022 482563.02E9 352563.02E9
2021 002563.02E9 352563.02E9
2020 10231.02E9 352563.02E9
2022 345633.25E9 352563.00E9
with
cal as
(
SELECT t.year,
cast(sum(t.profit) as decimal(17,2)) profit ,
cast(sum(t.gross)as decimal(17,2)) gross
-- t.srad_agency_provision - LAG(t.srad_agency_provision) OVER(ORDER BY year) as diff_previous_year
from shoptable t
group by t.year
order by t.year
)
after this query I got following result
year profit gross
2020 22311834.85 1030294447.42
2021 26598154.02 1704662197.69
2022 25234347.02 1841593955.36
Now I want to subtract the profit and gross from previos one.like 2021 from 2022, 2020 from 2021 I am trying
diff as (
select
t.year,
t.profit,
(t.profit) - LAG(t.profit) OVER(ORDER BY year) as diff_profit
from
shop table t
)
but it is showing for whole each value in 2020 and so on. I want to subtract the results of group by clause which I got in previous query.
Expected output is
year profit gross diff_profit diff gross
2020 22311834.85 1030294447.42 4,286,319.17 674,367,750.27
2021 26598154.02 1704662197.69 -1363807 136,931,757.67
2022 25234347.02 1841593955.36
How can I achieve it?thank you

Should not you just apply you window function to the results of cal. Something along this lines:
-- sample data
with dataset(year, profit,gross) as (
values
('2020', 252563, 352563),
('2022', 102563, 352563),
('2021', 352563, 352563),
('2022', 482563, 352563),
('2021', 002563, 352563),
('2020', 10231 , 352563),
('2022', 345633, 352563)
),
cal as (
SELECT t.year,
sum(t.profit) profit ,
sum(t.gross) gross
from dataset t
group by t.year
order by t.year
)
-- query
select *,
(profit) - LAG(profit) OVER(ORDER BY year) as diff_profit,
(gross) - LAG(gross) OVER(ORDER BY year) as diff_gross
from cal;

Related

How to calculate the difference in revenue for 2 specific periods from data in a single table?

I am using SQL Server 2014 and I have the following T-SQL query which is supposed to calculate the difference in total revenue based on 2 specific periods. The data to perform the computation come from a single SQL Table.
To summarize, I have a column in table T1 called Revenue and another column called Month. I need to find the difference in revenue for Months September 2020 to December 2020 versus September 2019 to December 2019.
My T-SQL query stands as follows:
USE [MyDatabase]
;with cte1 as
(
SELECT
sum ([Revenue]) as 'Revenue Sep 19 - Dec 19'
FROM
[T1]
WHERE
[Month] between '2019-09-01' and '2019-12-01'
),
cte2 as (
SELECT
sum ([Revenue]) as 'Revenue Sep 20 - Dec 20'
FROM
[T1]
WHERE
[Month] between '2020-09-01' and '2020-12-01'
),
cte3 as (
SELECT
cte2.[Revenue Sep 20 - Dec 20] as 'Total Revenue',
'Sep 20 - Dec 20' as 'Period',
'1' as 'ID'
FROM
[cte2]
UNION ALL
SELECT
cte1.[Revenue Sep 19 - Dec 19] as 'Total Revenue',
'Sep 19 - Dec 19' as 'Period',
'1' as 'ID'
FROM
[cte1]
)
select a.[Total Revenue] - b.[Total Revenue]
from
(select cte3.[Total Revenue] from [cte3] where cte3.[Period] = 'Sep 20 - Dec 20') a
JOIN
(select cte3.[Total Revenue] from [cte3] where cte3.[Period] = 'Sep 19 - Dec 19') b
ON b.[ID] = a.[ID]
I have based my query on the following: How to calculate between different group of rows of the same table
However, when running my query, I am getting the following error message:
Invalid column name 'ID'.
I can't figure out what I am doing wrong here. Isn't column ID present in the cte3?
ID must be present in the select list of both a and b for it to be visible to the join:
from
(select cte3.ID, cte3.[Total Revenue] from [cte3] where cte3.[Period] = 'Sep 20 - Dec 20') a
JOIN
(select cte3.ID, cte3.[Total Revenue] from [cte3] where cte3.[Period] = 'Sep 19 - Dec 19') b
ON b.[ID] = a.[ID]
You query is way more complicated than necessary.
SELECT (SUM(CASE WHEN [Month] between '2020-09-01' and '2020-12-01' THEN [Revenue] ELSE 0 END) -
SUM(CASE WHEN [Month] between '2019-09-01' and '2019-12-01' THEN [Revenue] ELSE 0 END)
) as difference
FROM [T1];
Your version has three CTEs, a UNION ALL, two subqueries, and a JOIN.
Notes:
Do not use single quotes to delimit column aliases. Only use single quotes for strings and dates.
Name your column aliases so they don't need to be escaped. For instance, use underscores instead of spaces.
Do not use meaningless table aliases such as a and b. Use meaningful ones, such as t2019.
While using SUB QUERY you must mention the Column which you mention in the Join condition also
SELECT A.[TOTAL REVENUE] - B.[TOTAL REVENUE]
FROM
(SELECT CTE3.[TOTAL REVENUE],CTE3.ID FROM [CTE3] WHERE CTE3.[PERIOD] = 'SEP 20 - DEC 20') A
JOIN
(SELECT CTE3.[TOTAL REVENUE],CTE3.ID FROM [CTE3] WHERE CTE3.[PERIOD] = 'SEP 19 - DEC 19') B ON B.[ID] = A.[ID]
Try this below code, It will help you
USE [MyDatabase]
select sum([Revenue Sep 20 - Dec 20])-sum([Revenue Sep 19 - Dec 19]) as revenue_diffrence
from
(
SELECT
[Revenue] as 'Revenue Sep 19 - Dec 19', 0 as 'Revenue Sep 20 - Dec 20'
FROM
[T1]
WHERE
[Month] between '2019-09-01' and '2019-12-01'
union all
SELECT
0 as 'Revenue Sep 19 - Dec 19',[Revenue] as 'Revenue Sep 20 - Dec 20'
FROM
[T1]
WHERE
[Month] between '2020-09-01' and '2020-12-01'
)a

Calculate YTD, Previous YTD in the same query

How could I calculate to calculate YTD and prior YTD in the same query?
I try to calculate weight and weight ytd. I didn't undestand how to add the calculation of prior ytd in the same query.
YTD: Sum Weight from the first of January to today
prior YTD: Sum
Weight from the first of January of last year to today minus 1 year
SELECT SUM(CASE
WHEN convert(date,s.Delivery_Year+'-'+ s.Delivery_month+'-'+ s.Delivery_day) BETWEEN dateadd(yy, DATEDIFF(yy, 0, GETDATE()), 0) AND GETDATE() THEN s.Weight
ELSE 0
END) AS WeightYTD ,
sum(Weight) AS weight ,
[Sales_Organization] ,
[Market_Grp] ,
[Delivery_Year] ,
[Delivery_month] ,
Delivery_day
FROM Fact_sales s
GROUP BY ,
[Sales_Organization] ,
[Market_Grp] ,
[Delivery_Year] ,
[Delivery_month] ,
Delivery_day
It's still quite unclear as to what results/behaviour you actually want.
One possible interpretation is that the "year to date" is relative to the date in the table, and not relative to "today" / GETDATE().
So...
for 14th March 2018 : YTD = 1st Jan 2018 to 14th March 2018
for 20th March 2019 : YTD = 1st Jan 2019 to 20th March 2019
That being the case, I would do the following to get the YTD column...
WITH
grouped_by_date AS
(
SELECT
[Sales_Organization],
[Market_Grp],
[Delivery_Year],
[Delivery_Month],
[Delivery_Day],
SUM([Weight]) AS Weight
FROM
Fact_sales s
GROUP BY
[Sales_Organization],
[Market_Grp],
[Delivery_Year],
[Delivery_Month],
[Delivery_Day]
),
cumulative_sum_for_ytd AS
(
SELECT
*,
SUM([Weight]) OVER (PARTITION BY [Delivery_Year]
ORDER BY [Delivery_Month], [Delivery_Day]
)
AS Weight_YTD
FROM
grouped_by_date
),
There isn't a LAG() function in SQL Server 2008, but there are "hacky tricks" that can simulate it...
hack_to_do_lag AS
(
SELECT
*,
CASE
WHEN [Delivery_Year]%2=1
THEN MAX(CASE WHEN [Delivery_Year]%2=0 THEN [Weight_YTD] END) OVER (PARTITION BY ([Delivery_Year]+0)/2)
ELSE MAX(CASE WHEN [Delivery_Year]%2=1 THEN [Weight_YTD] END) OVER (PARTITION BY ([Delivery_Year]+1)/2)
END
AS Weight_PreviousYTD
FROM
cumulative_sum_for_ytd
)
SELECT
*
FROM
hack_to_do_lag

SQL Server: finding change between selected month and previous month's sales

I have the following table for example. I would like to calculate the changes increase or decrease from previous month. This will show a percentage of change from previous month.
Location Month Sales
A Jan 1753
B Jan 32130
C Jan 71353
D Jan 74885
E Jan 50241
F Jan 66393
A Feb 80633
B Feb 67918
C Feb 73330
D Feb 33269
E Feb 78915
F Feb 98817
A Mar 80633
B Mar 67918
C Mar 73330
D Mar 33269
E Mar 78915
F Mar 98817
I wan to create a table like following. I searched stack overflow but was not able to get table.
Location Selected Current_Month Prvisous_Month Change
A Feb 80633 1753 4500%
B Feb 67918 32130 111%
C Feb 73330 71353 3%
D Feb 33269 74885 -56%
E Feb 78915 50241 57%
F Feb 98817 66393 49%
If you can't change the datatype of the "Month" column, for whatever reason, then this solution may work
DECLARE #Table TABLE ([Location] CHAR(1), [Month] NVARCHAR(3), Sales INT )
INSERT INTO #Table
([Location], [Month], Sales)
VALUES
('A',N'Jan',1753),
('B',N'Jan',32130),
('C',N'Jan',71353),
('D',N'Jan',74885),
('E',N'Jan',50241),
('F',N'Jan',66393),
('A',N'Feb',80633),
('B',N'Feb',67918),
('C',N'Feb',73330),
('D',N'Feb',33269),
('E',N'Feb',78915),
('F',N'Feb',98817),
('A',N'Mar',80633),
('B',N'Mar',67918),
('C',N'Mar',73330),
('D',N'Mar',33269),
('E',N'Mar',78915),
('F',N'Mar',98817)
DECLARE #Selection NVARCHAR(3) = N'Feb' -- Enter Selected Month here
;WITH cteX
AS(
SELECT
T.[Location]
, T.[Month]
, MonthNum = MONTH([T].[Month] + ' 1 1900') --Use some dummy date here
, T.Sales
FROM #Table T
)
SELECT
T.[Location]
, Selected = T.Month
, CurrentMonth = T.Sales
, PreviousMonth = T1.Sales
, Change = CAST((T.Sales - T1.Sales) / (T1.Sales * 1.0) * 100.0 AS DECIMAL)
FROM cteX T
INNER JOIN
cteX T1 ON T1.MonthNum = T.MonthNum - 1
AND T1.[Location] = T.[Location]
WHERE
T.[Month] = #Selection
Output
Something like this should be a good start. As Cool_Br33ze noted, you should rethink the date column of this table.
SELECT
*,
referenceTimePeriod / NULLIF(comparisonTimePeriod, 0) -- avoid DIV0 errors
FROM (
SELECT Location, Sales
FROM myTable
WHERE month = 'jan'
) AS referenceTimePeriod
FULL JOIN(
SELECT Location, Sales
FROM myTable
WHERE month = 'feb'
) AS comparisonTimePeriod ON referenceTimePeriod.Location = comparisonTimePeriod .Location
SELECT A.Location, 'Feb' AS Selected, A.Sales AS Current_Month
, B.Sales AS Prvisous_Month, (A.Sales - B.Sales)/ B.Sales AS Change
FROM YourTable A JOIN YourTable B
ON A.Month = B.Month + 1 -- You will have to represent Months by numbers
WHERE A.Month = 2 -- Selected month
Assuming that you change the Month attribute to date then you can use a LAG window function easily like this
SELECT location,
month,
sales,
lag(sales) over (order by month) previous,
(sales/lag(sales) over (order by month) - 1) * 100 as change
FROM your_table
WHERE month = 'feb'
The major issue now in your task is the correct ordering of Month which would be much more easier with date, or numbers.
EDIT: You can use the ordering solution of Cool_Br33ze for current data:
SELECT location,
month,
sales,
lag(sales) over (order by MONTH([T].[Month] + ' 1 1900')) previous, -- taken from Cool_Br33ze solution
(sales/lag(sales) over (order by MONTH([T].[Month] + ' 1 1900')) - 1) * 100 as change
FROM your_table
WHERE month = 'feb'
However, the best option is to change the data type of Month ...

SQL Year over year growth percentage from data same query

How do I calculate the percentage difference from 2 different columns, calculated in that same query? Is it even possible?
This is what I have right now:
SELECT
Year(OrderDate) AS [Year],
Count(OrderID) AS TotalOrders,
Sum(Invoice.TotalPrice) AS TotalRevenue
FROM
Invoice
INNER JOIN Order
ON Invoice.InvoiceID = Order.InvoiceID
GROUP BY Year(OrderDate);
Which produces this table
Now I'd like to add one more column with the YoY growth, so even when 2016 comes around, the growth should be there..
EDIT:
I should clarify that I'd like to have for example next to
2015,5,246.28 -> 346,15942029% ((R2015-R2014) / 2014 * 100)
If you save your existing query as qryBase, you can use it as the data source for another query to get what you want:
SELECT
q1.Year,
q1.TotalOrders,
q1.TotalRevenue,
IIf
(
q0.TotalRevenue Is Null,
Null,
((q1.TotalRevenue - q0.TotalRevenue) / q0.TotalRevenue) * 100
) AS YoY_growth
FROM
qryBase AS q1
LEFT JOIN qryBase AS q0
ON q1.Year = (q0.Year + 1);
Access may complain it "can't represent the join expression q1.Year = (q0.Year + 1) in Design View", but you can still edit the query in SQL View and it will work.
What you are looking for is something like this?
Year Revenue Growth
2014 55
2015 246 4.47
2016 350 1.42
You could wrap the original query a twice to get the number from both years.
select orders.year, orders.orders, orders.revenue,
(select (orders.revenue/subOrders.revenue)
from
(
--originalQuery or table link
) subOrders
where subOrders.year = (orders.year-1)
) as lastYear
from
(
--originalQuery or table link
) orders
here's a cheap union'd table example.
select orders.year, orders.orders, orders.revenue,
(select (orders.revenue/subOrders.revenue)
from
(
select 2014 as year, 2 as orders, 55.20 as revenue
union select 2015 as year, 2 as orders, 246.28 as revenue
union select 2016 as year, 7 as orders, 350.47 as revenue
) subOrders
where subOrders.year = (orders.year-1)
) as lastYear
from
(
select 2014 as year, 2 as orders, 55.20 as revenue
union select 2015 as year, 2 as orders, 246.28 as revenue
union select 2016 as year, 7 as orders, 350.47 as revenue
) orders

How use the operator IN with a subquery that returns two columns

Hello masters I need your help.
Having the table:
DataCollection
==================
PK Code
smallint RestaurantCode
smallint Year
tinyint Month
money Amount
money AccumulativeMonthsAmount
...
I need the AccumulateAmount for the LastMonth on every Restaurant.
First, I get the last Month per Restaurant for the 'current year'(for this case):
SELECT RestaurantCode, MAX(Month) as Month FROM DataCollection
WHERE (Year >= 2012 AND YEAR <= 2012) GROUP BY RestaurantCode
Now I want to use that as subquery, to get the Last - AccumulativeMonthsAmount :
SELECT AccumulativeMonthsAmount FROM DataCollection
WHERE (RestaurantCode, Month)
IN (SELECT RestaurantCode, MAX(Month) as Month FROM DataCollection
WHERE (Year >= 2012 AND YEAR <= 2012) GROUP BY RestaurantCode)
But the operator IN, don't work, How I should do it?
Sample Data sorted by Year and Month:
RestCode Amount Accumulative Year Month
123 343.3 345453.65 2012 12
123 124.7 345329.00 2012 11
...
122 312.2 764545.00 2012 12
122 123.4 764233.00 2012 11
...
999 500.98 2500.98 2012 6
999 100.59 2000.00 2012 5
...
I wanna to get the Accumulative for the last month of every restaurant:
RestCode Accumulative Month
123 345453.65 12
122 764545.00 12
99 2500.98 6
...
SELECT dc.AccumulativeMonthsAmount
FROM dbo.DataCollection AS dc
INNER JOIN
(
SELECT RestaurantCode, MAX(Month)
FROM dbo.PAL_Entries_Relatives
WHERE [Year] = 2012
GROUP BY RestaurantCode
) AS r(rc, m)
ON dc.RestaurantCode = r.rc
AND dc.[Month] = r.m;
With the changed requirements:
;WITH x AS
(
SELECT RestCode, Accumulative, [Month],
rn = ROW_NUMBER() OVER (PARTITION BY RestCode ORDER BY [Month] DESC)
FROM dbo.DataCollection -- or is it dbo.PAL_Entries_Relatives?
)
SELECT RestCode, Accumulative, [Month]
FROM x
WHERE rn = 1
ORDER BY [Month] DESC, RestCode DESC;
That syntax is not allowed in SQL Server. You can do something similar with EXISTS:
SELECT AccumulativeMonthsAmount
FROM DataCollection dc
WHERE exists (select 1
from PAL_Entries_Relatives er
where (Year >= 2012 AND YEAR <= 2012)
group by RestaurantCode
having er.RestaurantCode = dc.RestaurantCode and
max(er.month) = dc.Month
)
SELECT AccumulativeMonthsAmount
FROM DataCollection
INNER JOIN PAL_Entries_Relatives
ON DataCollection.RestaurantCode = PAL_Entries_Relatives.RestaurantCode
WHERE (Year >= 2012 AND YEAR <= 2012)
GROUP BY DataCollection.RestaurantCode
HAVING AccumulativeMonthsAmount.Month = MAX(PAL_Entries_Relatives.Month)