SQL Query - Year by Year Growth of Sales - sql-server

I have this result set from an SQL query. This result has come up by grouping the sales of tenant per year to get the total sales in yearly basis. Table is named TENANTSALES with columns: Tenant, date, sales etc.
TENANT YEAR SALES
tenant 1 2014 2000
tenant 1 2015 5000
tenant 2 2013 1000
tenant 2 2014 1500
tenant 2 2015 800
I used this SQL query code to achieve the above result
select tenant, year(date), SUM(sales)
from tenantSales
group by tenant, YEAR(date)
What I need to complete the task is to add a column name Yearly growth where it will compare and compute for year by year growth of sales per tenant. Here's the sample correct / desired output
TENANT YEAR SALES YEARLY GROWTH
tenant 1 2014 2000
tenant 1 2015 5000 150%
tenant 2 2013 1000
tenant 2 2014 1500 50%
tenant 2 2015 800 -46.67%
The formula is: ((Latest Year - Previous Year) / Previous Year) * 100
Example for Tenant 1:
((2015 sales - 2014 sales) / 2014 sales) * 100 = 150%
Ive tried to do this, adding the next year in the previous year's row to make it easy for me to compute for the two years sales, but I can't add up the sales of the latest year, only the year itself. Is there any way or proper way to do it?
select tenantcode, year(date), SUM(gsc), year(date) + 1
from dailymod
where tenantcode = 'cmbina13'
group by tenantcode, YEAR(date)
Your expert advice will be highly appreciated. Thanks

Try this query:
SELECT t1.tenant, t1.YEAR, t1.SALES,
CASE WHEN t2.YEAR IS NOT NULL THEN
FORMAT(
CONVERT(DECIMAL(10, 2), (t1.SALES - t2.SALES)) /
CONVERT(DECIMAL(10, 2), t2.SALES), 'p')
ELSE NULL END AS "YEARLY GROWTH"
FROM
(
SELECT tenant, YEAR(date) AS YEAR, SUM(sales) AS SALES
FROM tenantSales
GROUP BY tenant, YEAR(date)
) t1
LEFT JOIN
(
SELECT tenant, YEAR(date) AS YEAR, SUM(sales) AS SALES
FROM tenantSales
GROUP BY tenant, YEAR(date)
) t2
ON t1.tenant = t2.tenant AND t2.YEAR = t1.YEAR - 1
Click the link below for a working demo:
SQLFiddle
Late Update:
You could also try the same approach using Common Table Expressions. Here is what the above query would look like using this approach:
WITH cte AS(SELECT tenant, YEAR(date) AS YEAR, SUM(sales) AS SALES
FROM tenantSales
GROUP BY tenant, YEAR(date))
SELECT c1.*, CONVERT(varchar,
CONVERT(DECIMAL(10,2),
CONVERT(DECIMAL(10, 2), (c1.SALES - c2.SALES)) /
CONVERT(DECIMAL(10, 2), c2.SALES))) + '%' AS "YEARLY GROWTH"
FROM cte c1
LEFT JOIN cte c2 ON c1.tenant = c2.tenant AND c2.YEAR = c1.YEAR - 1
And here is another Fiddle where you can test this out:
SQLFiddle

Using cte you can reuse your query. I am using window function because you can have some missed years. But if years are consecutive then you can join directly on year column:
with cte as(select tenant,
year(date) y,
SUM(sales) s,
row_number() over(partition by tenant order by sum(sales)) rn
from tenantSales
group by tenant, YEAR(date))
select c1.*, ((c1.s - c2.s) / c2.s) * 100 as grouth
from cte c1
left join cte c2 on c1.tenant = c2.tenant and c1.rn = c2.rn + 1
Or:
with cte as(select tenant, year(date) y, SUM(sales) s
from tenantSales
group by tenant, YEAR(date))
select c1.*, ((c1.s - c2.s) / c2.s) * 100 as grouth
from cte c1
left join cte c2 on c1.tenant = c2.tenant and c1.y = c2.y + 1

please consider my answer with analytical function.
select tenant ,year(date) ,sum(sales),
format( ((sum(sales)/convert(float,lag(sum(sales)) over( partition by tenant order by tenant,year(date))))-1),'p')
from tenantSales
group by tenant , year(date)

select tenant_id
,year
,sales
, round((case when chk<>0 then ((sales-chk)/chk)*100 else 0 end),2)as yoy
from(
select tenant_id
,year
,sales
,lag(sales,1,0) over(partition by tenant_id order by year asc) as chk
from tenant)

Please find the below solution.
create table tenant_details
(tenant varchar(10),
year number(10),
sales number(10)
);
insert into tenant_details values ('tenant 1',2014,2000);
insert into tenant_details values ('tenant 1',2015,5000);
insert into tenant_details values ('tenant 2',2013,1000);
insert into tenant_details values ('tenant 2',2014,1500);
insert into tenant_details values ('tenant 2',2015,800);
commit;
SQL Query:
select tenant,year,sales,case when prev_sales is null then null else
to_char((sales-prev_sales)*100/prev_sales,9999.9) ||'%' end profit
from (
select tenant,year,sales ,lag(sales,1) over (partition by tenant order by year) prev_sales from tenant_details
);

Related

How to get the second to the highest monthly sales for every year

I'm trying to get the second to the highest monthly sales for every year.
So far I'm getting the second highest monthly sales for the first year only.
WITH newtable AS
(
SELECT
MONTH(o.orderdate) AS 'MONTH',
YEAR(o.orderdate) AS 'YEAR',
SUM(od.qty*od.unitprice) AS monthSales
FROM Sales.Orders AS o
INNER JOIN Sales.OrderDetails AS od
ON o.orderid = od.orderid
GROUP BY YEAR(o.orderdate), MONTH(o.orderdate)
)
SELECT YEAR, MAX(monthSales) AS secondHighestMonthlySales
FROM newtable
WHERE monthSales < (SELECT MAX(monthSales) FROM newtable)
GROUP BY YEAR;
I need the second highest for every year.
Assuming you have the data correct in newtable, focus on the second query regarding what you want. This is pure SQL:
Test Data:
Year Sales
2010 500
2010 400
2010 600
2011 700
2011 800
2011 900
2012 400
2012 600
2012 500
Query to select the second highest:
select O.year, max(O.sales) as secondhighestsale from Orders O,
(select year, max(sales) as maxsale
from Orders
group by year) A
where O. year = A.year
and O.sales < A.maxsale
group by O.year
Output:
year secondhighestsale
2010 500
2011 800
2012 500
As an alternative, you can use the ROWNUMBER() function. In this case, I use a common table expression. We find the second highest number of total sales for each year. These years are the so-called partitions.
USE TEMPDB
CREATE TABLE #Sales (SalesYear INT, TotalAmount INT)
INSERT INTO #Sales VALUES (2016, 1100), (2016, 700), (2016, 950),
(2017, 660), (2017, 760), (2017, 460),
(2017, 141), (2018, 999), (2018, 499);
WITH CTE AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY SalesYear ORDER BY TotalAmount DESC) AS RowNumb
FROM #Sales
)
SELECT SalesYear,
TotalAmount
FROM CTE WHERE RowNumb = 2

SQL Rows are not deleted from table when using where not exists and select distinct subquery

I am fairly new to SQL and have not been able to find what I'm doing wrong. I am trying to delete rows that have matching fields in 2 specific columns with other rows in the same table, for a temporary table so that only one row remains with the same ID and Year AND so that the row that remains is the one with the MAX date for those ID's in that Year.
My table:
ID Year Date
----------------------------------------------------
1 2017 01/05/2017
1 2017 11/17/2017
1 2017 08/07/2017
1 2016 03/22/2017
1 2016 04/01/2017
2 2017 03/12/2017
2 2016 02/03/2016
2 2016 04/19/2016
Desired results:
ID Year Date
----------------------------------------------------
1 2017 11/17/2017
1 2016 04/01/2016
2 2017 03/12/2017
2 2016 04/19/2016
What I have:
DELETE FROM #Temp
WHERE NOT EXISTS (
SELECT DISTINCT
t1.ID,
t1.Year
FROM #Temp AS t1,
#Temp AS t2
WHERE t1.ID = t2.ID
AND t1.Year <> t2.Year
GROUP BY t1.ID, t1.Year )
When I run it, nothing gets deleted but when I remove the first two lines to test what would be deleted, the results are correct so I'm really confused. I am working on the MAX function but want this part to work first.
Any help would be greatly appreciated! I have no idea what could be wrong with it.
one other way is to use row_number() function to find the duplicate records based on ID.. Year
delete t from
(
select *, row_number() over (partition by ID, [Year] order by date desc) rn from <table>
) t where t.rn > 1
with cte as
(
select row_number() over (partition by ID, Year order by date desc) as rn
)
delete * from cte where rn > 1;

Rolling Prior13 months with Current Month Sales

Within a SQL Server 2012 database, I have a table with two columns customerid and date. I am interested in getting by year-month, a count of customers that have purchased in current month but not in prior 13 months. The table is extremely large so something efficient would be highly appreciated. Results table is shown after the input data. In essence, it is a count of customers that purchased in current month but not in prior 13 months (by year and month).
---input table-----
declare #Sales as Table ( customerid Int, date Date );
insert into #Sales ( customerid, date) values
( 1, '01/01/2012' ),
( 1, '04/01/2013' ),
( 1, '01/01/2014' ),
( 1, '01/01/2014' ),
( 1, '04/06/2014' ),
( 2, '04/01/2014' ),
( 3, '01/03/2012' ),
( 3, '01/03/2014' ),
( 4, '01/04/2012' ),
( 4, '04/04/2013' ),
( 5, '02/01/2010' ),
( 5, '02/01/2013' ),
( 5, '04/01/2014' )
select customerid, date
from #Sales;
---desired results ----
yearmth monthpurchasers monthpurchasernot13m
201002 1 1
201201 3 3
201302 1 1
201304 2 2
201401 2 1
201404 3 2
Thanks very much for looking at this!
Dev
You didn't provide the expected result, but I believe this is pretty close (at least logically):
;with g as (
select customerid, year(date)*100 + month(date) as mon
from #Sales
group by customerid, year(date)*100 + month(date)
),
x as (
select *,
count(*) over(partition by customerid order by mon
rows between 13 preceding and 1 preceding) as cnt
from g
),
y as (
select mon, count(*) as cnt from x
where cnt = 0
group by mon
)
select g.mon,
count(distinct(g.customerid)) as monthpurchasers,
isnull(y.cnt, 0) as cnt
from g
left join y on g.mon = y.mon
group by g.mon, y.cnt
order by g.mon
Tell me if this query helps. It extracts all the rows which meet your condition into a Table variable. Then, I use your query and join to this table.
declare #startDate datetime
declare #todayDate datetime
declare #tbl_Custs as Table(customerid int)
set #startDate = '04/01/2014' -- mm/dd/yyyy
set #todayDate = GETDATE()
insert into #tbl_Custs
-- purchased only this month
select customerid
from Sales
where ([date] >= #startDate and [date] <= #todayDate)
and customerid NOT in
(
-- purchased in past 13 months
select distinct customerid
from Sales
where ([date] >= DATEADD(MONTH,-13,[date])
and [date] < #startDate)
)
-- your query goes here
select year(date) as year
,month(date) as month
,count(distinct(c.customerid)) as monthpurchasers
from #tbl_Custs as c right join
Sales as s
on c.customerid = s.customerid
group by year(date) , month(date)
order by year(date) , month(date)
Below query will produce what you are looking for. I am not sure how performance will be on a big table (how big is your table?) but it is pretty straight forward so I think it will be ok. I simply calculate the 13 months earlier on CTE to find my sale window. Than join to the Sales table within that window / customer id and grouping records based on the unmatched records. You don't actually need 2 CTE's here you can do the DATEADD(mm,-13,date) on the join part of the second CTE but I thought it might be more clear this way.
P.S. If you need to change the time frame from 13 months to something else all you have to change is the DATEADD(mm,-13,date) this simply substracts 13 months from the date value.
Hope this helps or at least leads to a better solution
;WITH PurchaseWindow AS (
select customerid, date, DATEADD(mm,-13,date) minsaledate
FROM #Sales
), JoinBySaleWindow AS (
SELECT a.customerid, a.date,a.minsaledate,b.date earliersaledate
FROM PurchaseWindow a
LEFT JOIN #sales b ON a.customerid =b.customerid
--Find the sales for the customer within the last 13 months of original sale
AND b.date BETWEEN a.date AND a.minsaledate
)
SELECT DATEPART(yy,date) AS [year], DATEPART(mm, date) AS [month], COUNT(DISTINCT customerid) monthpurchases
FROM JoinBySaleWindow
--Exclude records where a sale within last 13 months occured
WHERE earliersaledate IS NULL
GROUP BY DATEPART(mm, date), DATEPART(yy,date)
Sorry about the typos they are fixed now.

SQL Server: Top 10 salespeople per week - and previous rankings

select *
from
(
select year,
week,
salesperson,
count(*) as transactions,
rank() over(partition by week order by count(*) desc) as ranking
from sales
where year = '2010',
group by year,
week,
salesperson
) temp
where ranking <= 10
The query returns a list of the top 10 salespeople (in terms of # of transactions) for each week of the year.
How can I go about adding columns to my results for:
Previous week's ranking for that
salesperson
Total weeks in the Top 10 this year
Consecutive weeks in the Top 10 (starting at week 1)
Consecutive weeks in the Top 10 (starting in previous year, if possible)
Can you give any general advice on how to go about these sorts of problems?
PS: Using SQL server 2008
Actually, I'm not convinced that Views are the best way to go. You can do this sort of logic in CTE's and combine the entire thing into a single query. For example, here is what I have for everything except the consecutive logic:
;With
SalesDateParts As
(
Select DatePart(wk, SaleDate) As WeekNum, DatePart(yy, SaleDate) As [Year], SalesPersonId
From #Sales
)
, SalesByWeek As
(
Select [Year], WeekNum, SalesPersonId, Count(*) As SaleCount
, RANK() OVER( PARTITION BY [Year], [WeekNum] ORDER BY Count(*) DESC ) As SaleRank
From SalesDateParts
Group By [Year], WeekNum, SalesPersonId
)
, PrevWeekTopSales As
(
Select [Year], [WeekNum], SalesPersonId, SaleCount
From SalesByWeek
Where [Year] = DatePart(yyyy, DateAdd(d, -7, CURRENT_TIMESTAMP))
And WeekNum = DatePart(wk, DateAdd(d, -7, CURRENT_TIMESTAMP))
)
, WeeksInTop10 As
(
Select SalesPersonId, Count(*) As Top10Count
From SalesByWeek
Where SaleRank <= 10
Group By SalesPersonId
)
Select *
From Salespersons
Left Join WeeksInTop10
On WeeksInTop10.SalesPersonId = SalesPersons.SalesPersonId
Left Join PrevWeekTopSales
On PrevWeekTopSales.SalesPersonId = SalesPersons.SalesPersonId
The logic for "consecutive" is probably going to require a calendar table which contains a value for every day along with columns for the given date's year and week.
My advice is to do the other queries separately in views and then join them in by saleperson (which I assume is key)
The logic is this query is nice and clean and easy to follow. Otherwise - I think the way to attack this would be to start writing TSQL functions to calculate the other values, but I think those functions will have the queries in them anyway.

Get total from CTE query

I have the following query:
WITH Years AS
(
SELECT 2004 AS [Year]
UNION
SELECT 2005
UNION
SELECT 2006
UNION
SELECT 2007
UNION
SELECT 2008
)
SELECT *
FROM Years
Result:
Years >> Alias Column
2005
2006
2007
2008
That will return the years. Based on those years, I need to get the total # of orders from AdventureWorks2012.Sales.SalesOrderHeader
How should I code it? Union? I should not modify my 1st code, but need to add something right after it, so when I select the codes, F5 (execute), it should show me the totals. Also, at the end, there should be a string 'Total Orders:' added with the sum of all the years from the CTE.
Here is the result:
Year TotalOrders
----------------------------
2005 1379
2006 3692
2007 12443
2008 13951
Total Orders: 31465
First, you should get the count where Year of Orderdate in AdventureWorks2012 is equal to your year in CTE, then group it by Year - this is the CTE "temp" from my query below. Then sum the count and add the 'Total Orders:' by Union.
temp AS
(
SELECT CAST([Year] AS Varchar(15)) AS [Year],
COUNT(*) AS [TotalOrders]
FROM Years
INNER JOIN AdventureWorks2012.Sales.SalesOrderHeader
ON DATEPART(YEAR, OrderDate) = Year
GROUP BY [Year]
)
SELECT [Year],
[TotalOrders]
FROM temp
UNION
SELECT 'Total Orders: ',
SUM([TotalOrders])
FROM temp

Resources