Adding in zeroed rows for years without data T-SQL - sql

I have a large dataset with sales data that looks something like
***Category - Salesperson - Customer - Year - Jun - Jul .....***
Summary - Candice - R.Zeek - 2016 - $100 - $10
Summary - Candice - R.Zeek - 2017 - $40 - $50
Shirts - Candice - R.Zeek - 2016 - $80 - $10
Shirts - Candice - R.Zeek - 2017 - $40 - $50
Pants - Candice - R.Zeek - 2016 - $20 - $0
The problem is that there is no 2017 line for pants in this example. Since all the sales were in Shirts for 2017.
This has led to a scenario where if a salesperson doesn't have any sales data for a specific Category/Salesperson/Customer/Year combination then it doesn't have a line.
This makes the sales report look bad since different salespersons will have a different number of rows. I want uniformity so how do I get a line for 2017 in the example where the sales for Jun, Jul are just null or 0.
To add on to this post. The following 2 queries
Select distinct Year from #FinalResultTable
Select distinct CategorySequence, SalesPersonIdFromCustomer, CustomerNumber, Year from #FinalResultTable
Produces 1. The number of distinct years. In my case 2015, 2016, 2017, 2018. And 2. The sales data that I have that needs the additional rows
Year
2015
2016
2017
2018
CategorySequence SalesPersonIdFromCustomer CustomerNumber Year
1 SP000032 C000703 2016
1 SP000032 C000703 2017
1 SP000032 C000710 2016
1 SP000032 C000710 2017
1 SP000032 C000710 2018
Edit 2.
I added the following code
Insert into #FinalResultTable
Select S1.CategorySequence, SRT.CategoryDescription, S2.SalesPersonIdFromCustomer, SRT.SalesPersonName, S3.CustomerNumber, SRT.CustomerName, SRT.CustomerAddress, SRT.PercentageOrAmount,
S4.Year, SRT.Jun, SRT.Jul, SRT.Aug, SRT.Sep, SRT.Oct, SRT.Nov, SRT.Dec, SRT.Jan, SRT.Feb, SRT.Mar, SRT.Apr, SRT.May,
Coalesce(SRT.Jun,0) + Coalesce(SRT.Jul,0) + Coalesce(SRT.Aug,0) + Coalesce(SRT.Sep,0) + Coalesce(SRT.Oct,0) + Coalesce(SRT.Nov,0) + Coalesce(SRT.Dec,0) +
Coalesce(SRT.Jan,0) + Coalesce(SRT.Feb,0) + Coalesce(SRT.Mar,0) + Coalesce(SRT.Apr,0) + Coalesce(SRT.May,0)
From (select distinct CategorySequence from #SelectResultTable) S1
Cross join (select distinct SalesPersonIdFromCustomer from #SelectResultTable) S2
Cross join (select distinct CustomerNumber from #SelectResultTable) S3
Cross join (select distinct year from #SelectResultTable) S4
Left join #SelectResultTable SRT on
SRT.CategorySequence = S1.CategorySequence and
SRT.SalesPersonIdFromCustomer = S2.SalesPersonIdFromCustomer and
SRT.CustomerNumber = S3.CustomerNumber and
SRT.Year = S4.Year
Select * from #FinalResultTable
Order by SalesPersonIdFromCustomer, CustomerNumber, CategorySequence, PercentageOrAmount, Year
Return
It works but the problem is that All the SRT columns (For example Category description) are all Null. So do I need to do an update and traverse the table again to get all the information or can i modify the insert to handle that?

You can generate the rows using cross join and then join in the information that exists:
select c.Category, sp.Salesperson, t.Customer, y.Year
t.Jun, t.Jul
from (select distinct salesperson from t) sp cross join
(select distinct category c from t) c cross join
(select distinct year from t) as y left join
t
on t.salesperson = sp.salesperson and t.category = c.category and t.year = y.year;
If you want zeroes instead of NULLs, then use COALESCE(t.Jun, 0) as Jun` and so on.

Related

calculate CAGR using SQL

I have a dataset which looks like below
ADVERTISER YR REVENUE
---------------------------------
Altus Dental 2015 5560.00
Altus Dental 2016 48295.00
Altus Dental 2017 39920.00
I'm trying to find CAGR - year over year and taking an average of them, meaning
CAGR = (((REVENUE(2016)/REVENUE(2015)) - 1) + ((REVENUE(2017)/REVENUE(2016)) - 1) ) / 2
And Finally I will need an output something like this
ADVERTISER CAGR
--------------------
Altus Dental 3.75
How can I accomplish this in SQL? Please help me in providing an effective solution for this.
Calculate the CAGR (revenue/prev_revenue - 1) for each year and calculate the average CAGR (assume your dbms supports the LAG function)
select advertiser, avg(cagr) as CAGR
from
(
select advertiser, yr, revenue, revenue/prev_revenue - 1 as cagr
from
(select *, lag(revenue, 1) over
(partition by advertiser order by yr) as prev_revenue
from test ) t
) t1
group by advertiser
Here is one way:
select advertiser,
(((t16.revenue/t15.revenue) - 1) + ((t17.revenue/t16.revenue) - 1) ) / 2 as cagr
from t t15 join
t t16
on t15.advertiser = t16.advertiser and t15.yr = 2015 and t16.yr = 2016 join
t t17
on t15.advertiser = t17.advertiser and t17.yr = 2017
I'm assuming there won't be "holes" in the list of years. This should work for n years, and n advertisers:
SELECT advertiser,
SUM(revenue) / (COUNT(*) - 1) AS CAGR
FROM (SELECT advertiser,
COALESCE((revenue/revenue_old - 1), 0) as revenue
FROM (SELECT s.advertiser,
s.revenue,
LAG(s.revenue, 1) OVER(PARTITION BY s.advertiser
ORDER BY s.yr) AS revenue_old
FROM table_1 s))
GROUP BY advertiser;

SQL Server 2012 - find duplicate month (string) but different year

Having difficulty getting my head around this one.
I've been asked to create a report showing customers who signed up in the same month in previous year.
Invoice table looks a bit like this: (can't figure out how to create a nicer table)
invoiceid customerid monthinvoice yearinvoice
1 50 July 2016*
2 51 July 2016
3 52 July 2016*
4 53 July 2016
5 54 August 2016
6 50 July 2017*
7 51 August 2017
8 52 July 2017*
9 53 August 2017
10 54 September 2017
The only proper date column used is date the invoice was generated and the date payment received.
The records marked with * are the ones I'm only interested in, I just want to see 2 records returned when I pass a month as a parameter (I'll be asked to show how many customers have renewed in August for example. If the 1st invoice was in July 2016 and next invoice in August 2017 they will be treated as a new customer, not a renewal (must be exactly 12 months))
1) 50
2) 52
Any help much appreciated.
Here is one way. First we get all invoices for this month, current year, then union to the same month of the previous year. Then, we filter on customers who have a record for both using HAVING.
;with cte as(
select *
from yourtable
where
(monthinvoice = #monthinvoice
and yearinvoice = datepart(year,getdate()))
union
select *
from yourtable
where
(monthinvoice = #monthinvoice
and yearinvoice = datepart(year,dateadd(year,-1,getdate()))))
select *
from cte
where customerid in (select customerid from cte group by customerid having count(invoiceid) > 1)
I think this should do the trick for you-
SELECT I1.invoiceid, I1.customerid, I1.monthinvoice, I1.yearinvoice, I2.yearinvoice
FROM Invoice_table I1
INNER JOIN Invoice table I2
ON I1.customerid = I2.customerid
AND I1.monthinvoice = I2.monthinvoice
AND I1.yearinvoice = I2.yearinvoice + 1
something like this
select customerid , monthinvoice from yourtable
where yearinvoice in (2016, 2017) and monthinvoice = 'July'
group by customerid , monthinvoice
having count(*) = 2
Something like the following should give you some ideas as to how to build the report out.
Declare #ReportYear as int = 2017;
--this should show all customers with invioices for these months in both 2017 and 2016
select a.customerid, a.monthinvoice
from
(
--get people with invoice last year
Select distinct customerid, monthinvoice
from Invoices i0
where yearinvoice = #ReportYear - 1
) a
join
(
--get people with invoice this year
Select distinct customerid, monthinvoice
from Invoices i0
where yearinvoice = #ReportYear
) b on a.customerid = b.customerid
and a.monthinvoice = b.monthinvoice
If Im following your question correctly...
SELECT customerid FROM InvTblName T
INNER JOIN (SELECT customerID
FROM InvTblName
HAVING Z.invyear=T.invyear+1) Z
ON T.invmonth=Z.invmonth

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

sql query to calculate monthly growth percentage

I need to build a query with 4 columns (sql 2005).
Column1: Product
Column2: Units sold
Column3: Growth from previous month (in %)
Column4: Growth from same month last year (in %)
In my table the year and months have custom integer values. For example, the most current month is 146 - but also the table has a year (eg 2011) column and month (eg 7) column.
Is it possible to get this done in one query or do i need to start employing temp tables etc??
Appreciate any help.
thanks,
KS
KS,
To do this on the fly, you could use subqueries.
SELECT product, this_month.units_sold,
(this_month.sales-last_month.sales)*100/last_month.sales,
(this_month.sales-last_year.sales)*100/last_year.sales
FROM (SELECT product, SUM(units_sold) AS units_sold, SUM(sales) AS sales
FROM product WHERE month = 146 GROUP BY product) AS this_month,
(SELECT product, SUM(units_sold) AS units_sold, SUM(sales) AS sales
FROM product WHERE month = 145 GROUP BY product) AS last_month,
(SELECT product, SUM(units_sold) AS units_sold, SUM(sales) AS sales
FROM product WHERE month = 134 GROUP BY product) AS this_year
WHERE this_month.product = last_month.product
AND this_month.product = last_year.product
If there's a case where a product was sold in one month but not another month, you will have to do a left join and check for null values, especially if last_month.sales or last_year.sales is 0.
I hope I got them all:
SELECT
Current_Month.product_name, units_sold_current_month,
units_sold_last_month * 100 / units_sold_current_month prc_last_month,
units_sold_last_year * 100 / units_sold_current_month prc_last_year
FROM
(SELECT product_id, product_name, sum(units_sold) units_sold_current_month FROM MyTable WHERE YEAR = 2011 AND MONTH = 7) Current_Month
JOIN
(SELECT product_id, product_name, sum(units_sold) units_sold_last_month FROM MyTable WHERE YEAR = 2011 AND MONTH = 6) Last_Month
ON Current_Month.product_id = Last_Month.product_id
JOIN
(SELECT product_id, product_name, sum(units_sold) units_sold_last_year FROM MyTable WHERE YEAR = 2010 AND MONTH = 7) Last_Year
ON Current_Month.product_id = Last_Year.product_id
I am slightly guessing as the structure of the table provided is the result table, right? You will need to do self-join on month-to-previous-month basis:
SELECT <growth computation here>
FROM SALES s1 LEFT JOIN SALES s2 ON (s1.month = s2.month-1) -- last month join
LEFT JOIN SALES s3 ON (s1.month = s3.month - 12) -- lat year join
where <growth computation here> looks like
((s1.sales - s2.sales)/s2.sales * 100),
((s1.sales - s3.sales)/s3.sales * 100)
I use LEFT JOIN for months that have no previous months. Change your join conditions based on actual relations in month/year columns.

Changes to a Query Based on Year

select datepart(month,s1.Timeperiod) as monthofaum,
datepart(YEAR,s1.Timeperiod) as Yearofaum,
ISNULL(s2.endingAum,0) as Starting_Aum,
s1.endingAum as Ending_Aum
from #temp_1 s1
left outer join (select * from #temp_1)s2
on month(s1.Timeperiod) = dateadd(D,1,month(s2.Timeperiod))
This work perfectly for the Monthly basis, but supoose if i need to change the query to obtain the result based on the year also - where should i make changes?
Example
monthofaum Yearofaum Starting_Aum Ending_Aum
----------- ----------- --------------------- ---------------------
11 2009 0.00 0.00
12 2009 0.00 1059594254.86
1 2010 0.00 1083195051.98
2 2010 1083195051.98 1125314638.64
3 2010 1125314638.64 1212355911.70
4 2010 1212355911.70 1270374634.62
5 2010 1270374634.62 1265193377.27
6 2010 1265193377.27 1260776179.02
7 2010 1260776179.02 2599205697.44
8 2010 2599205697.44 1323838670.57
If you look at the data can see that for 2010 the Ending Aum value of previous month will be equal to Starting Aum of next month, but when it comes to year 2009 the dec Ending Aum is not assigned to Jan 2010 Starting Aum.
This is the bug which i need to fix.
To adjust it for year, compare just the month and year. For example,
select convert(varchar(7), getdate(), 120)
Prints 2010-10. Applied to your query, you could rewrite the on like:
on convert(varchar(7), s1.TimePeriod, 120) =
convert(varchar(7), DateAdd(year, 1, s2.TimePeriod), 120)
P.S. the subquery is not required. This line:
left outer join (select * from #temp_1)s2
is exactly the same as:
left outer join #temp_1 s2
This is assuming you don't care about the time I think this will work...
select datepart(month,s1.Timeperiod) as monthofaum,
datepart(YEAR,s1.Timeperiod) as Yearofaum,
ISNULL(s2.endingAum,0) as Starting_Aum,
s1.endingAum as Ending_Aum
from #temp_1 s1
left outer join (select * from #temp_1) s2
on s1.TimePeriod = DateAdd(year,1,s2.TimePeriod)
EDIT:
Or if you do care about time you could try this (I have a function for convenience)...
1.Create dateonly function
CREATE FUNCTION [dbo].[fn_DateOnly](#DateTime DATETIME)
-- Returns #DateTime at midnight; i.e., it removes the time portion of a DateTime value.
RETURNS DATETIME
AS
BEGIN
RETURN DATEADD(dd,0, DATEDIFF(dd,0,#DateTime))
END
2. This allows you to:
select datepart(month,s1.Timeperiod) as monthofaum,
datepart(YEAR,s1.Timeperiod) as Yearofaum,
ISNULL(s2.endingAum,0) as Starting_Aum,
s1.endingAum as Ending_Aum
from #temp_1 s1
left outer join (select * from #temp_1) s2
on dbo.fn_DateOnly(s1.TimePeriod) = DateAdd(year,1,dbo.fn_DateOnly(s2.TimePeriod))
select
datepart(month,s1.Timeperiod) as monthofaum,
datepart(YEAR,s1.Timeperiod) as Yearofaum,
ISNULL(s2.endingAum,0) as Starting_Aum,
s1.endingAum as Ending_Aum
from
#temp_1 s1
left outer join
(select * from #temp_1) s2 on (month(s1.Timeperiod)-1 = month(s2.Timeperiod)
or (month(s1.Timeperiod) = 1 and month(s2.Timeperiod) = 12))
The above query works if the table contains only data for 2 years. I will keep you all posted if I figure out the problem for above query