Get total from CTE query - sql-server

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

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 Rank function by week

I am still learning about SQL Server, and recently I encountered with issue with ranking (not sure if i should use rank).
I am trying to get the ranking to sort out as below, but i could not achieve by using row_number(over) or any ranking function in SQL server.
[Add in ]
the content in table1 has Cust_Code, Week and T_Mode, the one i display below is CustA. basically i want to know CustA, he is first using Air, then Water then switch to Air again in listed weeks.
**** Required Output ******
Week T_Mode Rank
201301 Air 1
201303 Water 2
201305 Water 2
201306 Water 2
201311 Air 3
i used Row_Number but it does not give what I want.
select *
, row_number()over(partition by T_Mode order by week) as Rank
from table1
the output returned
Week T_Mode Rank
201301 Air 1
201303 Water 1
201305 Water 2
201306 Water 3
201311 Air 2
any advice would be most welcome. Thank you!
Declare #t table(Week1 int,T_Mode varchar(20))
insert into #t values(201301,'Air'),(201303,'Water'),(201305,'Water'),(201306,'Water'),(201311,'Air')
;with cte as
(
select top 1 week1,t_mode,1 [Rank] from #t order by week1
union all
select b.week1,b.t_mode,case when a.T_Mode=b.T_Mode then a.Rank else a.Rank+1 end [Rank] from #t b
outer apply cte a
where b.Week1>a.week1 and b.T_Mode<>a.T_Mode
)
select distinct * from cte
Here's a solution that works in SQL Server 2012:
select
week, t_mode,
sum(change) over (order by week rows unbounded preceding) [rank]
from (
select *,
case when
(select top 1 t_mode
from table1
where week < t1.week
order by week desc) <> t1.t_mode then 1 else 0 end [change]
from table1 t1
) x
See this SQL Fiddle
Here is a less efficient version for SQL 2008:
;with cte as (
select *,
case when
(select top 1 t_mode
from table1
where week < t1.week
order by week desc) <> t1.t_mode then 1 else 0 end [change]
from table1 t1
)
select
week, t_mode,
(select sum(change) from cte where week <= x.week) [rank]
from cte x
And here's the fiddle.

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.

Resources