List all dates between range - sql

I've seen this asked before, but couldn't get an answer that worked.
I have 2 columns - startyear and endyear. I want to create a list of all years between the start and end year. So if the row has a startyear of 2010 and an endyear of 2016, I want the list to show 2010, 2011, 2012, etc.
Do I have to somehow grab the oldest startyear and newest endyear throughout all rows?
SELECT StartYear, EndYear
FROM TestYear

You can do this with a recursive CTE:
;With MaxMin As
(
Select Min(StartYear) As First,
Max(EndYear) As Last
From TestYear
), Years (Year) As
(
Select First From MaxMin Union All
Select Year + 1
From Years
Where Year < (Select Last From MaxMin)
)
Select *
From Years

Use a recursive cte to do this.
declare #startyear int = 2010; --or any other number or a select query
declare #endyear int = 2020; --or any other number or a select query
with years(yr) as
(
select #startyear
union all
select yr+1 from years where yr < #endyear
)
select * from years;

Try something like this in a Stored Procedure.
SELECT * INTO #FileDates
FROM (SELECT distinct year(startyear) as year from filename )
SELECT * INTO #FileDates2
FROM (SELECT distinct year(endyear) as year from filename )
select distinct year from #FileDate, #FileDate2
Also you might want to check out: http://www.sqlservercentral.com/stairway/119892/

Sample Data
Declare #t Table (StartYear INT , EndYear INT)
INSERT INTO #t Values (2010 , 2011), (2012 , 2016);
Query
WITH X AS (
Select MIN(StartYear) MinYear
,MAX(EndYear) MaxYear
FROM #t
)
Select TOP ((Select MaxYear FROM X)
- (Select MinYear FROM X) + 1)
ROW_Number() Over (Order by (Select NULL))
+ (Select MinYear FROM X) -1 rn
FROM master..spt_values a
Cross Join master..spt_values b

Related

SQL Server : count number of rows per hour

I have a table with 2628 rows in it. I want to get a count per hour. I have a completion_time column which tells the date time of per record.
I can find only one-hour count.
select count(*)
from billing b
where b.completion_time >= '2019-04-16 23:50:23'
and b.completion_time <='2019-04-17 00:50:22'
The date time is up to 9 hours. i.e. the process was started at 2016-04-16 23:50:23 and it ends on 2019-04-17 08:16:49. So I want total counts per hour.
Update 1
I want output like below
How can I achieve it? Any help would be highly appreciated.
Try this:
select datepart(hour,b.completion_time) Hour, count(*) NumberOfRecords
from billing b
group by datepart(hour,b.completion_time)
Edit:
select row_number() over (order by min(b.completion_time)) RowNumber, count(*) NumberOfRecords
from billing b
group by datepart(hour,b.completion_time)
order by min(b.completion_time)
try this.
Declare #StartTime DATETIME = '2016-04-16 23:50:23'
Declare #EndTime DateTime = '2019-04-17 08:16:49'
Declare #Tab Table(id int, Completion_Time DateTime)
Insert into #Tab
SELECT 1, '2016-04-16 23:50:23' Union All
SELECT 2,'2016-04-17 00:50:24' Union All
SELECT 3,'2016-04-17 01:50:26' Union All
SELECT 4,'2016-04-17 01:50:32' Union All
SELECT 5,'2016-04-17 01:50:55' Union All
SELECT 6,'2016-04-17 02:50:28' Union All
SELECT 7,'2016-04-17 02:50:30' Union All
SELECT 8,'2016-04-17 02:50:45' Union All
SELECT 9,'2016-04-17 04:50:32' Union All
SELECT 10,'2016-04-17 04:50:52'
--Select Id, DATEDIFF(HH,#StartTime,Completion_Time) Diff from #Tab
;with cte
As
(
SELECT CONVERT(VARCHAR(5),DATEDIFF(HH,#StartTime,Completion_Time)+1) as [Hour] , COUNT(*) As [Records]
From #Tab
Group by DATEDIFF(HH,#StartTime,Completion_Time)+1
)
Select [Hour] + CASE
WHEN [Hour] % 100 IN (11,12,13) THEN 'th'
WHEN [Hour] % 10 = 1 THEN 'st'
WHEN [Hour] % 10 = 2 THEN 'nd'
WHEN [Hour] % 10 = 3 THEN 'rd'
ELSE 'th'
END + ' hour',
Records
from cte
This will do just what you need :
SELECT HOUR(b.completion_time) hourOfDay, COUNT(*) NumberOfRecords
FROM billing b
GROUP BY hourOfDay
HOUR is a predefined function to calculate hour from a DateTime.

Years separated by comma CTE in SQL Server

I have a field called StartYear, the value of my field is 2000. I have another field called EndYear, the value of my field is 2005.
I want to create a field called YearsInTheProgram that has the values 2005,2004, 2003,2002,2001,2000.
Each of my rows have different values, so in essence I would like this field to have the difference of my fields separated my commas.
I was able to find something that would work, but this would give me a value in different rows. However, I want all of them in one row.
with CTE as
(
select datepart(year, '2006-12-25') as yr
union all
select yr + 1
from CTE
where yr < datepart(year, '2013-11-14')
)
select yr
from CTE
declare #tmp varchar(250)
SET #tmp = ''
;with CTE as
(
select datepart(year, '2006-12-25') as yr
union all
select yr + 1
from CTE
where yr < datepart(year, '2013-11-14')
)
select #tmp = #tmp + convert(varchar(500), yr) + ', ' from CTE
select SUBSTRING(#tmp, 0, LEN(#tmp)) as yr
You can bulid up your string of years using this CTE.
DECLARE #STARTYEAR varchar(10) = '2006-12-25'
DECLARE #ENDYEAR varchar(10) = '2013-11-14'
;with CTE as
(
select datepart(year, #STARTYEAR) as yr, CAST(datepart(year, #STARTYEAR) AS VARCHAR(MAX)) as c
union all
select yr + 1 as yr, CAST(concat(c, ',', yr+1) AS VARCHAR(MAX)) as c
from CTE
where yr < datepart(year, #ENDYEAR)
)
select yr, c
from CTE
where (yr = datepart(year, #ENDYEAR))
And here it is with StartYear and EndYear taken from DB table.
create table #yr (id int, start varchar(20), stop varchar(20))
insert into #yr values(1,'2005-01-01','2010-12-10'), (2,'2008-01-01','2011-12-10'), (3,'2007-01-01','2013-12-10'), (4,'2009-01-01','2012-10-10')
;with CTE as
(
select start as start, datepart(year, start) as yr, CAST(datepart(year, start) AS VARCHAR(MAX)) as c from #yr
union all
select CTE.start as start, yr + 1 as yr, CAST(concat(c, ',', yr+1) AS VARCHAR(MAX)) as c
from CTE join #yr on #yr.start = CTE.start
where yr < datepart(year, stop)
)
select id, #yr.start, #yr.stop, c
from CTE join #yr on #yr.start = CTE.start
where (yr = datepart(year, stop))
With following result
4 2009-01-01 2012-10-10 2009,2010,2011,2012
3 2007-01-01 2013-12-10 2007,2008,2009,2010,2011,2012,2013
2 2008-01-01 2011-12-10 2008,2009,2010,2011
1 2005-01-01 2010-12-10 2005,2006,2007,2008,2009,2010

Count total students actively studying during date range

I am looking to get one output showing the total count of students actively studying in each year in a user defined range.
for eg DB Structure
StudentId Course StartDate EndDate
1 BSc Maths 2012-01-01 2015-01-01
2 BSc English 2014-01-01 2017-01-01
If the user defines actively studying between '2013' and '2016' the output i would like to get is this;
YEAR Student_Count
2013 1
2014 2
2015 2
2016 1
Thanks for your time :)
You need to have a years table to do this.
Here am generating years table through Recursive CTE. You can create a physical years table and use it instead of recursive CTE.
DECLARE #max_yr INT = (SELECT Max(Year(EndDate)) yr FROM yourtable);
WITH cte
AS (SELECT Min(Year(StartDate)) yr
FROM yourtable
UNION ALL
SELECT yr + 1
FROM cte
WHERE yr < #max_yr)
SELECT a.yr as [YEAR],
Count(1) as [Student_Count]
FROM cte a
JOIN yourtable b
ON a.yr BETWEEN Year(b.StartDate) AND Year(b.EndDate)
AND a.yr BETWEEN 2013 AND 2016
GROUP BY a.yr
You could try this, it certainly worked for me - obviously #startYear and #endYear would be replaced by your own variables but I've left them in so you understand how the code is working:
Create Table #Temp2 ([YEAR] int, Student_Count int)
Declare #startYear int = '2013',
#endYear int = '2016'
while (#startYear <= #endYear)
Begin
Insert into #Temp2
Select #startYear [YEAR], count(studentId)
from table
where Cast(Cast(#startYear as varchar) as date) between StartDate and EndDate
Set #startYear = #startYear + 1
End
Select * from #Temp2

What is the best way to find next n week days

I got the following code from the following question I asked:
Passing in Week Day name to get nearest date in SQL
I need to find next 4 Weekdays based on today's date for corresponding Day-Of-Week in my table ie, if today is 2015-01-24 the result should be 1/24, 1/31, 2/7, 2/14 for Saturdays.
TABLE
SAMPLE QUERY
create table #t
(
jobId int,
personId int,
frequencyVal varchar(10)
);
insert into #t values (1,100,'Mondays'),(2,101,'Saturdays');
WITH cte(n) AS
(
SELECT 0
UNION ALL
SELECT n+1 FROM cte WHERE n < 3
)
select #t.jobId, #t.personId, #t.frequencyVal, STUFF(a.d, 1, 1, '') AS FutureDates
from #t
cross apply (SELECT CASE #t.frequencyVal
WHEN 'SUNDAYS' THEN 1
WHEN 'MONDAYS' THEN 2
WHEN 'TUESDAYS' THEN 3
WHEN 'WEDNESDAYS' THEN 4
WHEN 'THURSDAYS' THEN 5
WHEN 'FRIDAYS' THEN 6
WHEN 'SATURDAYS' THEN 7
END)tranlationWeekdays(n)
cross apply (select ',' + CONVERT(varchar(10), CONVERT(date,dateadd(WEEK, cte.n,CONVERT(DATE, DATEADD(DAY, (DATEPART(WEEKDAY, GETDATE()) + tranlationWeekdays.n) % 7, GETDATE()))))) from cte FOR XML PATH('')) a(d);
drop table #t;
EXPECTED RESULT
Gets the first day of current month.
DECLARE #FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
Create the table and insert values
create table #t
(
jobId int,
personId int,
frequencyVal varchar(10)
);
insert into #t values (1,100,'Mondays'),(2,101,'Saturdays');
You can use either of the below queries for your situation.
QUERY 1 : Select the first 4 week of days in current month for particular week day
-- Gets the first day of current month
DECLARE #FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
;WITH CTE as
(
-- Will find all dates in current month
SELECT #FIRSTDAY as DATES
UNION ALL
SELECT DATEADD(DAY,1,DATES)
FROM CTE
WHERE DATES < DATEADD(MONTH,1,#FIRSTDAY)
)
,CTE2 AS
(
-- Join the #t table with CTE on the datename+'s'
SELECT jobId,personId,frequencyVal, DATES,
ROW_NUMBER() OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES) ORDER BY CTE.DATES) DATECNT
FROM CTE
JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal
WHERE MONTH(DATES)= MONTH(GETDATE())
)
-- Converts to CSV and make sure that only 4 days are generated for month
SELECT DISTINCT C2.jobId,C2.personId,frequencyVal,
SUBSTRING(
(SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
FROM CTE2
WHERE C2.jobId=jobId AND C2.personId=personId AND DATECNT<5
ORDER BY CTE2.DATES
FOR XML PATH('')),2,200000) futureDates
FROM CTE2 C2
SQL FIDDLE
For example, in Query1 the nearest date(here we take example as Saturday) of
2015-Jan-10 will be 01/03,01/10,01/17,01/24
2015-Jan-24 will be 01/03,01/10,01/17,01/24
QUERY 2 : Select nearest 4 week of days in current month for particular week day
-- Gets the first day in current month
DECLARE #FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
;WITH CTE as
(
-- Will find all dates in current
SELECT CAST(#FIRSTDAY AS DATE) as DATES
UNION ALL
SELECT DATEADD(DAY,1,DATES)
FROM CTE
WHERE DATES < DATEADD(MONTH,1,#FIRSTDAY)
)
,CTE2 AS
(
-- Join the #t table with CTE on the datename+'s'
SELECT jobId,personId,frequencyVal,DATES,
-- Get week difference for each weekday
DATEDIFF(WEEK,DATES,GETDATE()) WEEKDIFF,
-- Count the number of weekdays in a month
COUNT(DATES) OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES)) WEEKCOUNT
FROM CTE
JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal
WHERE MONTH(DATES)= MONTH(GETDATE())
)
-- Converts to CSV and make sure that only nearest 4 week of days are generated for month
SELECT DISTINCT C2.jobId,C2.personId,frequencyVal,
SUBSTRING(
(SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
FROM CTE2
WHERE C2.jobId=jobId AND C2.personId=personId AND C2.frequencyVal=frequencyVal AND
((WEEKDIFF<3 AND WEEKDIFF>-3 AND WEEKCOUNT = 5) OR WEEKCOUNT <= 4)
ORDER BY CTE2.DATES
FOR XML PATH('')),2,200000) futureDates
FROM CTE2 C2
SQL FIDDLE
For example, in Query2 the nearest date(here we take example as Saturday) of
2015-Jan-10 will be 01/03,01/10,01/17,01/24
2015-Jan-24 will be 01/10,01/17,01/24,01/31
QUERY 3 : Select next 4 week's dates for particular week day irrelevant of month
;WITH CTE as
(
-- Will find all dates in current month
SELECT CAST(GETDATE() AS DATE) as DATES
UNION ALL
SELECT DATEADD(DAY,1,DATES)
FROM CTE
WHERE DATES < DATEADD(DAY,28,GETDATE())
)
,CTE2 AS
(
-- Join the #t table with CTE on the datename+'s'
SELECT jobId,personId,frequencyVal, DATES,
ROW_NUMBER() OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES) ORDER BY CTE.DATES) DATECNT
FROM CTE
JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal
)
-- Converts to CSV and make sure that only 4 days are generated for month
SELECT DISTINCT C2.jobId,C2.personId,frequencyVal,
SUBSTRING(
(SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
FROM CTE2
WHERE C2.jobId=jobId AND C2.personId=personId AND C2.frequencyVal=frequencyVal
AND DATECNT < 5
ORDER BY CTE2.DATES
FOR XML PATH('')),2,200000) futureDates
FROM CTE2 C2
SQL FIDDLE
The following would be the output if the GETDATE() (if its Saturday) is
2015-01-05 - 1/10, 1/17, 1/24, 1/31
2015-01-24 - 1/24, 1/31, 2/7, 2/14
This is a simpler way I think, and I think it fits your requirements
Note that I have changed your frequency_val column to an integer that represents the day of the week from SQL servers perspective and added a calculated column to illustrate how you can easily derive the day name from that.
/*************************************************/
--Set up our sample table
/*************************************************/
declare #t table
(
jobId int,
personId int,
--frequencyVal varchar(10) -- why store a string when a tiny int will do.
frequency_val tinyint,
frequency_day as datename(weekday,frequency_val -1) + 's'
)
insert into #t
values
(1,100,1),--'Mondays'),
(2,101,6),--'Saturdays');
(3,101,7),--'Sundays');
(4,100,2)--'Tuesdays'),
--select * from #t
/*************************************************/
--Declare & initialise variables
/*************************************************/
declare #num_occurances int = 4
declare #from_date date = dateadd(dd,3,getdate()) -- this will allow you to play with the date simply by changing the increment value
/*************************************************/
-- To get a row for each occurance
/*************************************************/
;with r_cte (days_ahead, occurance_date)
as (select 0, convert(date,#from_date,121)
union all
select r_cte.days_ahead +1, convert(date,dateadd(DD, r_cte.days_ahead+1, #from_date),121)
from r_cte
where r_cte.days_ahead < (7 * #num_occurances) -1
)
select t.*, r_cte.occurance_date
from
#t t
inner join r_cte
on DATEPART(WEEKDAY, dateadd(dd,##DATEFIRST - 1 ,r_cte.occurance_date)) = t.frequency_val
/*************************************************/
--To get a single row with a CSV of every occurance
/*************************************************/
;with r_cte (days_ahead, occurance_date)
as (select 0, convert(date,#from_date,121)
union all
select r_cte.days_ahead +1, convert(date,dateadd(DD, r_cte.days_ahead+1, #from_date),121)
from r_cte
where r_cte.days_ahead < (7 * #num_occurances) -1
)
select
t.*,
STUFF( (select ', '
+ convert(varchar(2),datepart(month,occurance_date),0) + '/'
+ convert(varchar(2),datepart(day,occurance_date),0) as occurance
from r_cte
where DATEPART(WEEKDAY, dateadd(dd,##DATEFIRST - 1 ,r_cte.occurance_date)) = t.frequency_val
FOR XML PATH (''),TYPE).value('.','varchar(30)')
,1,2,'') occurance_date -- rest of STUFF() function
from
#t t

Sql server issue ORDER BY clause is invalid in views

Here I am going to post my full stored procedure code then I will pick the area which is giving error.
ALTER PROC [dbo].[WarrantyTrends]
(
#StartYr AS INT,
#EndYr AS INT
)
AS
DECLARE #query varchar(max)
DECLARE #years varchar(max), #yearsColumns varchar(max)
SELECT 1 mID, 'January' as month into #tempMonths UNION ALL
SELECT 2,'February' as month UNION ALL
SELECT 3,'March' as month UNION ALL
SELECT 4,'April' as month UNION ALL
SELECT 5,'May' as month UNION ALL
SELECT 6,'June' as month UNION ALL
SELECT 7,'July' as month UNION ALL
SELECT 8,'August' as month UNION ALL
SELECT 9,'September' as month UNION ALL
SELECT 10,'October' as month UNION ALL
SELECT 11,'November' as month UNION ALL
SELECT 12,'December' as month
SELECT #years=COALESCE(#years+',','') +'['+ cast(years as varchar(4))+']',
#yearsColumns=COALESCE(#yearsColumns+',','') +'isnull(['+ cast(years as varchar(4))+'],0)
as ['+cast(years as varchar(4))+']'
from (select distinct YEAR(CurDate) years from EOD_Main
WHERE YEAR(CurDate)>=#StartYr AND YEAR(CurDate)<=#EndYr
) as x
SET #query = 'Select months,'+#yearsColumns+' from (
select distinct mID, YEAR(CurDate) years,[MONTH] months,
isnull(Warranty_Info,0) as Warranty_Info from EOD_Main
right join #tempMonths on datename(month,CurDate ) =[month]
) as xx
PIVOT
(
SUM(xx.Warranty_Info) FOR years IN ('+#years+')
)
as pvt ORDER BY mID'
PRINT #query
--EXEC(#query)
drop table #tempMonths
See this code
SELECT
#years = COALESCE(#years+',','') +'['+ cast(years as varchar(4))+']',
#yearsColumns = COALESCE(#yearsColumns+',','') +'isnull(['+ cast(years as varchar(4))+'], 0) as ['+cast(years as varchar(4))+']'
FROM
(SELECT distinct YEAR(CurDate) years
FROM EOD_Main
WHERE YEAR(CurDate) >= #StartYr AND YEAR(CurDate) <= #EndYr) as x
This inner most area
(SELECT distinct YEAR(CurDate) years
FROM EOD_Main
WHERE YEAR(CurDate) >= #StartYr AND YEAR(CurDate) <= #EndYr) as x**
I am not being able to order the year asc wise rather getting error.
In this area I want to generate year ascending wise.... so guide me what I can do. thanks
Order by is irrelevant in views. You will not automatically get teh records inteh right order. YOu need to havea an order by in all the sql code you want ordered.
Order by year(curDate)