Calculate profit of successive years by adding profit of previous year - sql

Sample data and expected result is provided in the image:
We need to add the profit of previous year with the successive year and display the data in the format given in he image (sample data is also provided in the image).
Please help me with the SQL query to solve this problem.

You can also write this using the window function.
SELECT
Year,
SUM(Profit) OVER(ORDER BY Year) AS Total_Profit
FROM your_table
ORDER BY Year

This is probably the world's simplest recursive CTE which you could have googled.
But here it is:
declare #years table(y int, p int)
insert #years values (2015,1000),(2016,2000),(2017,500),(2018,1000)
; with cumulative as
(
select top 1 * from #years order by y
union all
select y.y, y.p+c.p
from #years y
join cumulative c on y.y=c.y+1
)
select * from cumulative
Result:
y p
2015 1000
2016 3000
2017 3500
2018 4500

Use Sum over partition :
WITH V1 AS (
SELECT 2015 AS YEAR, 1000 AS PROFIT FROM DUAL
UNION ALL SELECT 2016 AS YEAR, 2000 AS PROFIT FROM DUAL
UNION ALL SELECT 2017 AS YEAR, 500 AS PROFIT FROM DUAL
UNION ALL SELECT 2018 AS YEAR, 1000 AS PROFIT FROM DUAL
)
SELECT
V1.YEAR
, PROFIT --- You can comment it if not needed
, SUM(PROFIT) OVER (PARTITION BY 1 ORDER BY YEAR RANGE UNBOUNDED PRECEDING) AS PROFIT_CUM
FROM V1;

Related

GROUP BY clause in SQL

Year Brand Amount
2018 Apple 45000
2019 Apple 35000
2020 Apple 75000
2018 Samsung 15000
2019 Samsung 20000
2020 Samsung 95000
2018 Nokia 21000
2019 Nokia 17000
2020 Nokia 14000
i want the expexted output to be like:
Year Brand Amount
2018 Apple 45000
2019 Apple 35000
2020 Samsung 95000
THIS IS WHAT I TRIED:
Select Year, Brand, Max(Amount)as HighestPrice
from Practice
Group by Year
but it shows error:
"Column 'Practice.Brand' is invalid in the select list because it is
not contained in either an aggregate function or the GROUP BY clause."
I would highly appreciate the help. Thanks
The error is happening because you need both in the group by. I would just write a standard select and join into the max amount in a sub query and correlate on year.
SELECT YEAR
,BRAND
,Amount AS HighestPrice
FROM Practice B
WHERE Amount = (SELECT MAX(Amount) FROM Practice A
WHERE A.YEAR = B.YEAR)
ORDER BY YEAR ASC
A generic SQL version would be:
select p.Year, p.Brand, p.Amount as HighestPrice
from Practice p
join (
select Year, max(Amount) as Amount
from Practice
group by Year
) as m on m.Amount=p.Amount
You can use partition, example in ssms:
SELECT Year, Brand, Amount as HighestPrice
FROM(
SELECT
Year, Brand, Amount,
RANK()OVER (PARTITION BY year ORDER BY Amount DESC) AS rn
FROM Practice
) a
WHERE rn = 1
ORDER BY year
You don't say which database you are using, so I'll assume it's PostgreSQL. You can use DISTINCT ON to get the row with the max amount per year.
For example:
select distinct on (year) * from practice order by year, amount desc
Or you can use cross apply:
select a.year, a.brand, b.amount
from yourtable a
cross apply (select max(amount) as amount from yourtable
where a.year = year
group by year
having max(amount) =a.amount
order by amount desc)b
Try this :
https://dbfiddle.uk/Cr6kFqaE
select XX.Year,Table1.Brand ,XX.Amount from
(
select Year, Max(Amount) as Amount from Table1 group by Year
)as XX
left outer join Table1 on Table1.Year= XX.Year and Table1.Amount = XX.Amount

Running Total in SQL including last year end total

Need a query to select "Running Total" as mentioned in the image
2017 year end total plus Every months new figure should add up to previous total.
https://i.stack.imgur.com/DL7p0.png "Example"
This following script will work for MSSQL and you can use the same logic for other databases as well-
WITH your_table(year,month,partersgrowth)
AS
(
SELECT '2019','jan', 100 UNION ALL
SELECT '2019','feb', 300 UNION ALL
SELECT '2019','mar', 400 UNION ALL
SELECT '2019','apr', 500 UNION ALL
SELECT '2018','Dec', 200
)
SELECT A.year,A.month,A.partersgrowth,
(
SELECT SUM(B.partersgrowth)
FROM your_table B
WHERE CAST(B.Year +'-'+B.month+'-01' AS DATE)
<= CAST(A.Year +'-'+A.month+'-01' AS DATE)
) Running_Total
FROM your_table A
ORDER BY CAST(A.Year +'-'+A.month+'-01' AS DATE)
using #mkRabbani solution.. you can simplify it like this:
;WITH your_table(year,month,partersgrowth)
AS
(
SELECT '2019','jan', 100 UNION ALL
SELECT '2019','feb', 300 UNION ALL
SELECT '2019','mar', 400 UNION ALL
SELECT '2019','apr', 500 UNION ALL
SELECT '2018','Dec', 200
)
select *, sum(partersgrowth) over ( order by [year],[month]) as running_total
from your_table
EDIT: As pointed out by comment below.. you want to order by a proper date in the sum part ( I would use order by year and then the month number rather than the month name)
If you use MSSQL you can run the following code
with cte as (
select t.*, lag(partersGrowth) over(partition by year order by month asc rows ROWS UNBOUNDED PRECEDING) prevTotal
from Table )
select years, month, partersGrowth, prevTotal || '+' partersGrowth as "Need Running Total"
from cte

SQL join for comparing this year vs last year sales by store and by product

I'm trying to compare this year vs last sales by store and by product.
The idea behind my SQL is to create a base table for 24 months rolling data and perform a join on transaction date - 1 year. This is somewhat complicated that my data is aggregated by date, by store and by product.
My code is as below. And my issue is that when i do a left join, the numbers for this year and last year doesn't match up. For example, feb-19 last year sales should equal to feb-18 this year sales, but I am not getting this result.
My guess is that last year has certain stores and products that are not available this year but I have no idea how to resolve this. I tried a full join, but the numbers are also off. Appreciate any feedback please!
-- extract sales by day, store, product
select business_date, store_code, product_code,
sum(sales) as sales
into #temp1
from sales_table
where business_date >= date_trunc('month', dateadd('month', -24, sysdate))
group by business_date, store_code, product_code;
-- compare this year against last year
select ty.*, ly.sales as sales_ly
into #temp2
from #temp1 ty left join #temp1 ly
on ty.product_code = ly.product_code
and ty.store_code = ly.store_code
and trunc(dateadd(year, -1, ty.business_date)) = ly.business_date;
-- check
select to_char(business_date, 'yyyymm'), sum(sales) ty, sum(sale_ly) as ly
from #temp2
group by to_char(business_date,'yyyymm')
order by 1;
You have join - trunc(dateadd(year, -1, ty.business_date)) = ly.business_date;
You are trying to compare sales of particular day of this year with last year (22/02/2019 with 22/02/2018)
In your table, do you have data for both of these days, aggregated sample data from your table could help in writing the query.
Query -
with sales_tab as (
select '2019/02/22' as date1, 123 as store, 456 as prod, 20 sales from dual
union
select '2019/02/23' as date1, 123 as store, 456 as prod, 30 as sales from dual
union
select '2019/02/24' as date1, 123 as store, 456 as prod, 40 sales from dual
union
select '2018/02/22' as date1, 123 as store, 456 as prod, 60 sales from dual
union
select '2018/02/23' as date1, 123 as store, 456 as prod, 70 as sales from dual
union
select '2018/02/25' as date1, 123 as store, 456 as prod, 80 sales from dual)
select
t1.date1, t2.date1, t1.store, t1.prod, t1.sales this_year, t2.sales prev_year
from sales_tab t1 left outer join sales_tab t2 on
(t1.store=t2.store
and t1.prod=t2.prod
and cast(substr(t1.date1,1,4) as int)-1=cast(substr(t2.date1, 1,4) as int)
and substr(t1.date1,6,2)=substr(t2.date1,6,2)
and substr(t1.date1,9,2)=substr(t2.date1,9,2)
);

POSTGRES - Average for previous 4 weekdays

Hi I am trying to calculate the average of previous 4 Tuesdays. I have daily sales data and I am trying to calculate what the average for previous 4 weeks were for the same weekday.
Attached is a snapshot of how my dataset looks like
Now for March 6, I would like to know what is the average for the previous 4 weeks were, (namely Feb 6, Feb 13, Feb 20 and Feb 27). This value needs to be assigned to Monthly Average column
I am using a PostGres DB.
Thanks
You can use window functions:
select t.*,
avg(dailycount) over (partition by seller_name, day
order by date
rows between 3 preceding and current row
) as avg_4_weeks
from t
where day = 'Tuesday';
This assumes that "previous 4 weeks" is the current date plus the previous three weeks. If it starts the week before, only the windowing clause needs to change:
select t.*,
avg(dailycount) over (partition by seller_name, day
order by date
rows between 4 preceding and 1 preceding
) as avg_4_weeks
from t
where day = 'Tuesday';
I decided to post my answer also, for anyone else searching. My answer will allow you to put in any date and get the average for the previous 4 weeks ( current day + previous 3 weeks matching the day).
SQL Fiddle
PostgreSQL 9.3 Schema Setup:
CREATE TABLE sales (sellerName varchar(10), dailyCount int, saleDay date) ;
INSERT INTO sales (sellerName, dailyCount, saleDay)
SELECT 'ABC',10,to_date('2018-03-15','YYYY-MM-DD') UNION ALL /* THIS ONE */
SELECT 'ABC',11,to_date('2018-03-14','YYYY-MM-DD') UNION ALL
SELECT 'ABC',12,to_date('2018-03-12','YYYY-MM-DD') UNION ALL
SELECT 'ABC',13,to_date('2018-03-11','YYYY-MM-DD') UNION ALL
SELECT 'ABC',14,to_date('2018-03-10','YYYY-MM-DD') UNION ALL
SELECT 'ABC',15,to_date('2018-03-09','YYYY-MM-DD') UNION ALL
SELECT 'ABC',16,to_date('2018-03-08','YYYY-MM-DD') UNION ALL /* THIS ONE */
SELECT 'ABC',17,to_date('2018-03-07','YYYY-MM-DD') UNION ALL
SELECT 'ABC',18,to_date('2018-03-06','YYYY-MM-DD') UNION ALL
SELECT 'ABC',19,to_date('2018-03-05','YYYY-MM-DD') UNION ALL
SELECT 'ABC',20,to_date('2018-03-04','YYYY-MM-DD') UNION ALL
SELECT 'ABC',21,to_date('2018-03-03','YYYY-MM-DD') UNION ALL
SELECT 'ABC',22,to_date('2018-03-02','YYYY-MM-DD') UNION ALL
SELECT 'ABC',23,to_date('2018-03-01','YYYY-MM-DD') UNION ALL /* THIS ONE */
SELECT 'ABC',24,to_date('2018-02-28','YYYY-MM-DD') UNION ALL
SELECT 'ABC',25,to_date('2018-02-22','YYYY-MM-DD') UNION ALL /* THIS ONE */
SELECT 'ABC',26,to_date('2018-02-15','YYYY-MM-DD') UNION ALL
SELECT 'ABC',27,to_date('2018-02-08','YYYY-MM-DD') UNION ALL
SELECT 'ABC',28,to_date('2018-02-01','YYYY-MM-DD')
;
Now For The Query:
WITH theDay AS (
SELECT to_date('2018-03-15','YYYY-MM-DD') AS inDate
)
SELECT AVG(dailyCount) AS totalCount /* 18.5 = (10(3/15)+16(3/8)+23(3/1)+25(2/22))/4 */
FROM sales
CROSS JOIN theDay
WHERE extract(dow from saleDay) = extract(dow from theDay.inDate)
AND saleDay <= theDay.inDate
AND saleDay >= theDay.inDate-INTERVAL '3 weeks' /* Since we want to include the entered
day, for the INTERVAL we need 1 less week than we want */
Results:
| totalcount |
|------------|
| 18.5 |

How to find the missing rows?

I have a table as shown in the image.
The column MONTH_NO should be having months from 1 to 12 for every year. For some years, we missed to load data for some months. I need a query which will fetch the years which doesn't have all the 12 months along with the missing month number.
Please help.
For example -
with mth
as (select level as month_no
from dual
connect by level <= 12),
yrs as (select distinct year from rag_month_dim)
select m.year, m.month_no
from (select year, month_no
from yrs, mth) m,
rag_month_dim r
where m.year = r.year(+)
and m.month_no = r.month_no(+)
group by m.year, m.month_no
having max(r.month_no) is null
order by year, month_no
Try it like this:
post this into an empty query window and adapt to your needs.
MyData contains a "full" year 2013, Sept is missing in 2014 and June and Sept are missing in 2015.
DECLARE #OneToTwelve TABLE(Nmbr INT)
INSERT INTO #OneToTwelve VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12);
DECLARE #myData TABLE(yearNo INT, MonthNo INT)
INSERT INTO #myData VALUES
(2013,1),(2013,2),(2013,3),(2013,4),(2013,5),(2013,6),(2013,7),(2013,8),(2013,9),(2013,10),(2013,11),(2013,12)
,(2014,1),(2014,2),(2014,3),(2014,4),(2014,5),(2014,6),(2014,7),(2014,8),(2014,10),(2014,11),(2014,12)
,(2015,1),(2015,2),(2015,3),(2015,4),(2015,5),(2015,7),(2015,8),(2015,10),(2015,11),(2015,12);
WITH AllYears AS
(
SELECT DISTINCT yearNo FROM #myData
)
,AllCombinations AS
(
SELECT *
FROM #OneToTwelve AS months
CROSS JOIN AllYears
)
SELECT *
FROM AllCombinations
LEFT JOIN #myData AS md ON AllCombinations.Nmbr =md.MonthNo AND AllCombinations.yearNo=md.yearNo
WHERE md.MonthNo IS NULL
select distinct year, m.lev
from rag_month_dim a
join
(
select level lev
from dual
connect by level <= 12
) m
on 1=1
minus
select year, month_no
from rag_month_dim
order by 1, 2
select *
from (select count (-1) total, year from rag_month_dim group by year) as table
where total < 12.
you got a year that doesnt have 12 month data and total month record in your data.