Here is what my table looks like
ID | Date
------------
13 2013-03-21 00:00:00
13 2013-03-23 00:00:00
13 2013-03-24 00:00:00
25 2013-03-21 00:00:00
25 2013-03-22 00:00:00
25 2013-03-21 00:00:00
25 2013-03-23 00:00:00
25 2013-03-28 00:00:00
25 2013-03-21 00:00:00
82 2013-03-22 00:00:00
82 2013-03-22 00:00:00
I want it to output (combine sat & sun into 'weekend'). It'll combine all the Mondays, Tuesdays, Wednesdays, etc. and combine the values.
day | total
--------------
Friday 2
Thursday 3
Weekend 3
This is my query so far
SELECT
CASE
WHEN DATENAME(weekday, [date]) = 'Saturday' OR DATENAME(weekday, [date]) = 'Sunday' THEN 'Weekend'
ELSE DATENAME(weekday, [date])
END AS Day,
COUNT(DISTINCT [id]) AS score
FROM [table]
GROUP BY
CASE
WHEN DATENAME(weekday, [date]) = 'Saturday' OR DATENAME(weekday, [date]) = 'Sunday' THEN 'Weekend'
ELSE DATENAME(weekday, [date])
END
Currently I can get my query to output this, but it seems like it isn't adding both thursdays or sat + sun.
Day | total
--------------
Friday 2
Thursday 2
Weekend 2
Appears like what you are actually after is a distinct count of the ID and date combined. Seems like the easiest way, therefore, would be to concatenate the 2 values to make a new unique value and COUNT those DISTINCT values:
SELECT CASE WHEN DATENAME(WEEKDAY, V.[Date]) IN ('Saturday','Sunday') THEN 'Weekend' ELSE DATENAME(WEEKDAY, V.[Date]) END AS [Weekday],
COUNT(DISTINCT CONCAT(ID,[date]))
FROM (VALUES (13, CONVERT(datetime2(0),'2013-03-21T00:00:00')),
(13, CONVERT(datetime2(0),'2013-03-23T00:00:00')),
(13, CONVERT(datetime2(0),'2013-03-24T00:00:00')),
(25, CONVERT(datetime2(0),'2013-03-21T00:00:00')),
(25, CONVERT(datetime2(0),'2013-03-22T00:00:00')),
(25, CONVERT(datetime2(0),'2013-03-21T00:00:00')),
(25, CONVERT(datetime2(0),'2013-03-23T00:00:00')),
(25, CONVERT(datetime2(0),'2013-03-28T00:00:00')),
(25, CONVERT(datetime2(0),'2013-03-21T00:00:00')),
(82, CONVERT(datetime2(0),'2013-03-22T00:00:00')),
(82, CONVERT(datetime2(0),'2013-03-22T00:00:00'))) V (ID, [Date])
GROUP BY CASE WHEN DATENAME(WEEKDAY, V.[Date]) IN ('Saturday','Sunday') THEN 'Weekend' ELSE DATENAME(WEEKDAY, V.[Date]) END;
Try this!
select count(id)as Total,Day from
(
select id, case when Datename(weekday,dt1) IN('Saturday','Sunday') then 'Weekend' else Datename(weekday,dt1) end as 'Day' from tab
)x
group by Day
Demo here
select Day,Count(day) cnt from (
SELECT
CASE
WHEN DATENAME(weekday, [date]) = 'Saturday' OR DATENAME(weekday, [date]) = 'Sunday' THEN 'Weekend'
ELSE DATENAME(weekday, [date])
END AS Day
,[date]
FROM [Infinite_campus].[dbo].[AttendanceRecord]
) as t
group by Day
Related
Input table
Start time End Time
8/12/14 17:00 8/14/14 12:00
I need to show the output as below
Date 11:00 to 23:00 23:00 to 11:00
8/12/14 6 1
8/13/14 12 12
8/14/14 1 11
I know you all will say "What did you try ?"
But the answer "I Can't think of where to start"
Using recursive CTE:
SQL Fiddle
MS SQL Server 2012 Schema Setup:
Query 1:
DECLARE #StartDate DateTime = '2014-08-12 17:00'
DECLARE #EndDate DateTime = '2014-08-14 12:00'
;WITH CTE
AS
(
SELECT #StartDate AS [Date],
CAST(#StartDate As time) AS [Time]
UNION ALL
SELECT DATEADD(HH, 1, [Date]) As [Date],
CAST(DATEADD(HH, 1, [Date]) AS Time) AS [Time]
FROM CTE
WHERE DATEADD(HH, 1, [Date]) < #EndDate
)
SELECT CAST([Date] AS Date) [Date],
SUM(CASE WHEN [Time] BETWEEN '11:00' AND '22:59'
THEN 1 ELSE 0 END) as [11:00 to 23:00],
SUM(CASE WHEN [Time] BETWEEN '00:00' AND '10:59'
THEN 1
WHEN [Time] Between '23:00' and '23:59' THEN 1
ELSE 0 END) as [23:00 to 11:00]
FROM CTE
GROUP BY CAST([Date] AS Date)
ORDER BY [Date]
Results:
| DATE | 11:00 TO 23:00 | 23:00 TO 11:00 |
|------------|----------------|----------------|
| 2014-08-12 | 6 | 1 |
| 2014-08-13 | 12 | 12 |
| 2014-08-14 | 1 | 11 |
The following shows one way to do with using a Tally tables (aka "table of numbers" table.)
Key assumptions I made:
Start and End times are both known
Start and End times are for precise hours (e.g. 1:00, 2:00, and not 13:23 or 00:00.00.007). (This is why I used the smalldatetime datatype)
End time is always greater than Start time
I went further and assumed that the data is stored in a table for multiple entities--that is, you'd ultimately want to process multiple items in one query. If you only ever want to do this for one item at a time, (a) the query below can be readily chopped down, and (b) it'd probably be easier to do in C# or whatever the calling language is.
Setting up testing data:
-- DROP TABLE Testing
CREATE TABLE Testing
(
EntryId int not null
,StartTime smalldatetime not null
,EndTime smalldatetime not null
)
INSERT Testing values
(1, 'Aug 12, 2014 17:00', 'Aug 14, 2014 12:00') -- Original problem
,(2, 'Aug 11, 2014 00:00', 'Aug 11, 2014 23:00') -- 23 hours
,(3, 'Aug 11, 2014 00:00', 'Aug 12, 2014 00:00') -- 24 hour shift
,(4, 'Aug 11, 2014 12:00', 'Aug 12, 2014 12:00') -- Noon to Noon
,(11, 'Aug 22, 2014 4:00', 'Aug 22, 2014 5:00') -- One-hour problem cases
,(12, 'Aug 22, 2014 10:00', 'Aug 22, 2014 11:00') -- One-hour problem cases
,(13, 'Aug 22, 2014 11:00', 'Aug 22, 2014 12:00') -- One-hour problem cases
,(14, 'Aug 22, 2014 12:00', 'Aug 22, 2014 13:00') -- One-hour problem cases
,(21, 'Aug 23, 2014 18:00', 'Aug 23, 2014 19:00') -- One-hour problem cases
,(22, 'Aug 23, 2014 22:00', 'Aug 23, 2014 23:00') -- One-hour problem cases
,(23, 'Aug 23, 2014 23:00', 'Aug 24, 2014 00:00') -- One-hour problem cases
,(24, 'Aug 24, 2014 00:00', 'Aug 24, 2014 1:00') -- One-hour problem cases
My routine:
DECLARE
#Earliest smalldatetime
,#Latest smalldatetime
-- This could be thrown in as a first CTE, but doing so would make the overall query that much less comprehensible.
SELECT
#Earliest = min(StartTime)
,#Latest = max(EndTime)
from Testing
--where <filtering criteria, if you're not parsing the whole table)
;WITH
Pass0 as (select 1 as C union all select 1), --2 rows
Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows
Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows
Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows
Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows
Tally as (select row_number() over(order by C) as Number from Pass4),
DateRange as (select
dateadd(hh, ta.Number, #Earliest) ShiftHour
from Tally ta
where dateadd(hh, ta.Number, #Earliest) <= #Latest)
SELECT
te.EntryId
,cast(dateadd(hh, -1, dr.Shifthour) as date) [Date]
,sum(case when datepart(hh, dateadd(hh, -1, dr.Shifthour)) between 11 and 22 then 1 else 0 end) [11:00 to 23:00]
,sum(case when datepart(hh, dateadd(hh, -1, dr.Shifthour)) between 11 and 22 then 0 else 1 end) [23:00 to 11:00]
from Testing te
inner join DateRange dr
on dr.ShiftHour > te.StartTime
and dr.ShiftHour <= te.Endtime
group by
te.EntryId
,cast(dateadd(hh, -1, dr.Shifthour) as date)
order by
te.EntryId
,cast(dateadd(hh, -1, dr.Shifthour) as date)
Plug this in to show the results without the grouping, invaluable in figuring out what's going on in there:
;WITH
Pass0 as (select 1 as C union all select 1), --2 rows
Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows
Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows
Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows
Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows
Tally as (select row_number() over(order by C) as Number from Pass4),
DateRange as (select
dateadd(hh, ta.Number, #Earliest) ShiftHour
from Tally ta
where dateadd(hh, ta.Number, #Earliest) <= #Latest)
SELECT
te.EntryId
,dateadd(hh, -1, dr.Shifthour)
,cast(dateadd(hh, -1, dr.Shifthour) as date) [Date]
,datepart(hh, dateadd(hh, -1, dr.Shifthour))
from Testing te
inner join DateRange dr
on dr.ShiftHour > te.StartTime
and dr.ShiftHour <= te.Endtime
order by
te.EntryId
,cast(dateadd(hh, -1, dr.Shifthour) as date)
,dateadd(hh, -1, dr.Shifthour)
The hard parts were:
Getting the 24th hour (Aug 23, 00:00) into the previous day (Aug 24). That's why I shift everything back one day [edit] one HOUR
< and > logic when dealing with multiple entries
Reconciling with the fact that the results for a "One day Noon to Noon" sample just looks goofy.
Yeah, this may be over-engineering, but some times you just have to give it a try. I couldn't say how well it would perform on large datasets, and if your first and last dates are more than four years apart you'll need to add "Pass5" when building the tally table.
DECLARE #StartTime DATETIME='08/01/2013 00:00:00'
,#EndTime DATETIME='08/03/2013 23:00:00'
DECLARE #StartDate DATE,
#Starthour INT,
#Startmin INT,
#EndDate DATE,
#Endhour INT,
#Endmin INT
SELECT #StartDate=CONVERT(DATE,#StartTime,101),#Starthour=DATEPART(HOUR,#StartTime),#Startmin=DATEPART(MINUTE,#StartTime)
SELECT #EndDate=CONVERT(DATE,#EndTime,101),#Endhour=DATEPART(HOUR,#EndTime),#Endmin=DATEPART(MINUTE,#EndTime)
DECLARE #T TABLE (StartTime DATETIME, EndTime DATETIME)
INSERT #T VALUES (DATEADD(MINUTE,-#Startmin,#StartTime), DATEADD(MINUTE,-#Endmin,#EndTime))
DECLARE #Final1 TABLE (ShiftDate DATETIME,[11:00 to 23:00] INT,[23:00 to 11:00] INT)
DECLARE #Final2 TABLE (ShiftDate DATETIME,[11:00 to 23:00] INT,[23:00 to 11:00] INT)
DECLARE #Final3 TABLE (ShiftDate DATETIME,[11:00 to 23:00] INT,[23:00 to 11:00] INT)
;WITH CTE AS
(
SELECT StartTime AS dt, EndTime,StartTime
FROM #T
UNION ALL
SELECT DATEADD(MINUTE, 60, dt) AS dt, EndTime,DATEADD(MINUTE, 60,StartTime) AS StartTime
FROM CTE
WHERE dt < DATEADD(HOUR, -1, EndTime)
)
INSERT INTO #Final1([11:00 to 23:00],[23:00 to 11:00],ShiftDate)
SELECT
CASE
WHEN CAST(dt AS TIME) >= '11:00' AND CAST(dt AS TIME) < '23:00' THEN 1
ELSE 0
END AS '11:00 to 23:00',
CASE
WHEN CAST(dt AS TIME) >= '11:00' AND CAST(dt AS TIME) < '23:00' THEN 0
ELSE 1
END AS '23:00 to 11:00'
,StartTime
FROM CTE
INSERT INTO #Final2([11:00 to 23:00],[23:00 to 11:00],ShiftDate)
SELECT SUM([11:00 to 23:00])*60 AS [11:00 to 23:00]
,SUM([23:00 to 11:00])*60 AS [23:00 to 11:00]
,CONVERT(DATE,DATEADD(MINUTE,-660,ShiftDate)) AS ShiftDate
FROM #Final1
GROUP BY CONVERT(DATE,DATEADD(MINUTE,-660,ShiftDate))
INSERT INTO #Final3([11:00 to 23:00],[23:00 to 11:00],ShiftDate)
SELECT CASE WHEN ShiftDate=#StartDate AND #Starthour BETWEEN 11 AND 22 THEN [11:00 to 23:00]-#Startmin ELSE [11:00 to 23:00] END AS [11:00 to 23:00],
CASE WHEN ShiftDate=#StartDate AND #Starthour BETWEEN 23 AND 10 THEN [23:00 to 11:00]-#Startmin ELSE [23:00 to 11:00] END AS [23:00 to 11:00],
ShiftDate
FROM #Final2
SELECT ShiftDate,
CONVERT(VARCHAR(10),
(CASE WHEN ShiftDate=#EndDate AND #Endhour BETWEEN 11 AND 22
THEN [11:00 to 23:00]+#Endmin
ELSE [11:00 to 23:00] END)) AS [11:00 to 23:00],
CONVERT(VARCHAR(10),
(CASE WHEN ShiftDate=#EndDate AND #Endhour BETWEEN 23 AND 10
THEN [23:00 to 11:00]+#Endmin
ELSE [23:00 to 11:00] END)) AS [23:00 to 11:00]
FROM #Final3
I am tryig to write what must be a fairly common audit report; number of rows added to a table over time; reported back against previous cycles to understand the trends in the data.
I have a table that audits creation of rows in the database. It has a field RowEnteredDate date time. I am looking to create an audit report Week/ Month/ Current Quarter / Year.
In my head I am looking at this as multiple passes over the data around the dates; which is quite costly in my database. My reasoning at the moment is
I started out with working out the dates for my year / month / quarter
set datefirst 1
declare #dateranges table (
rangelabel varchar(100),
startdate datetime,
enddate datetime,
myrowcount integer identity(1,1)
)
insert into #dateranges (Rangelabel, startdate, enddate)
select
'Current Year',
DATEADD(yy, DATEDIFF(yy,0,GETDATE()), 0),
DATEADD(ms,-3,DATEADD(yy, DATEDIFF(yy,0,GETDATE() )+1, 0))
insert into #dateranges (Rangelabel, startdate, enddate)
select
'Current Quarter',
DATEADD(qq, DATEDIFF(qq,0,GETDATE()), 0),
DATEADD(qq, DATEDIFF(qq, - 1, getdate()), - 1)
insert into #dateranges (Rangelabel, startdate, enddate)
select
'Current Month',
DATEADD(month, DATEDIFF(month, 0, getdate()), 0),
DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0))
If my table is tblOfUsefullFacts and my date row is RowEnteredDate what is the best way to get the aggregate; broken by Day.
Date Range Mon Tues Wed Thu Fri Sat Sun
Year To date 12000 13000 12000 3200 98000 8900 4000
Quarter 1 302 407 201 97 1732 120 37
Month ...
I can get the totals by day easily enough using a query like this
select
count(*) ,
datepart(weekday, RowEnteredDate)
from
tblOfUsefullFacts aa
Where
datepart(weekday, RowEnteredDate) is not null
group by datepart(weekday, RowEnteredDate)
order by datepart(weekday, RowEnteredDate) sac
This selects the data out row by row; which i could pivot and loop round to get the data. Im slightly nervous as the real numbers are in the 10's of millions in them and would like to not impact the underlying processing if i can avoid it.
As i need to do this in multiple passes is there a lighter way to do this without running the loops to get the totals? Or a mechanism in SQL my fuzzy brain is ignoring.
This should give you an idea how to do it. Sorry for any syntax errors, it isn't tested.
;with cte as
(
select
d.rangelabel,
datepart(weekday, RowEnteredDate) as WkDay,
count(*) as RowCt
from tblOfUsefullFacts f
join #dateranges d on f.RowEnteredDate between d.startdate and d.enddate
Where datepart(weekday, RowEnteredDate) is not null
group by d.rangelabel,datepart(weekday, RowEnteredDate)
)
select
RangeLabel,
sum(case when WkDay = 1 then RowCt else 0 end) as Sunday,
sum(case when WkDay = 2 then RowCt else 0 end) as Monday,
sum(case when WkDay = 3 then RowCt else 0 end) as Tuesday,
sum(case when WkDay = 4 then RowCt else 0 end) as Wednesday,
sum(case when WkDay = 5 then RowCt else 0 end) as Thursday,
sum(case when WkDay = 6 then RowCt else 0 end) as Friday,
sum(case when WkDay = 7 then RowCt else 0 end) as Saturday
from cte
group by RangeLabel
EmployeeID RecordID DateRecord
1 1 2/19/2013 12:00:00 AM
1 2 2/21/2013 12:00:00 AM
1 3 2/23/2013 12:00:00 AM
1 4 2/27/2013 12:00:00 AM
1 5 3/3/2013 12:00:00 AM
2 11 3/10/2013 12:00:00 AM
2 12 3/14/2013 12:00:00 AM
1 14 3/16/2013 12:00:00 AM
How can I count the number of days?
Example in February 2013 which has "19, 21, 23, 27" that should be count to "4" days .. ??
I found this method ..
SELECT DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
DATEPART(dd, Daterecord),
COUNT(*)
FROM Records
GROUP BY DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
DATEPART(dd, Daterecord)
and resulted to ..
2013 2 19 1
2013 2 21 1
2013 2 23 1
2013 2 27 1
2013 3 3 1
2013 3 10 1
2013 3 14 1
2013 3 16 1
it just get the specific dates but didm't count the total number of days in each month .. help me .. pls
I have change few names hopr you won't mind
WITH Emp_CTE AS (
SELECT EmployeeID ,DATEPART(yy, Daterecord) AS years,
DATEPART(mm, Daterecord) AS months
-- DATEPART(dd, Daterecord) AS days
FROM testTrial
)
SELECT COUNT(months) AS noOfMonths ,* FROM Emp_CTE GROUP BY months,EmployeeID,years
SqlFiddle
Let you try this:-
1: Find the number of days in whatever month we're currently in
DECLARE #dt datetime
SET #dt = getdate()
SELECT #dt AS [DateTime],
DAY(DATEADD(mm, DATEDIFF(mm, -1, #dt), -1)) AS [Days in Month]Solution
2: Find the number of days in a given month-year combo
DECLARE #y int, #m int
SET #y = 2012
SET #m = 2
SELECT #y AS [Year],
#m AS [Month],
DATEDIFF(DAY,
DATEADD(DAY, 0, DATEADD(m, ((#y - 1900) * 12) + #m - 1, 0)),
DATEADD(DAY, 0, DATEADD(m, ((#y - 1900) * 12) + #m, 0))
) AS [Days in Month]
If your table is called Employee then this will do the trick:
select convert(varchar, DateRecord, 112)/ 100, count(*)
from Employee
group by convert(varchar, DateRecord, 112)/ 100
Your initial query was almost right, just needed to remove the DATEPART(dd, Daterecord) from the grouping and it would work. Add in a HAVING clause to find the records from the month of February:
SELECT
DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
COUNT(1)
FROM
Records
GROUP BY
DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord)
HAVING
DATEPART(yy, eCreationTime) = 2013
AND DATEPART(mm, Daterecord) = 2
there is no 'yearmonth' in the suggested code ??
try this perhaps
select
datename(month,daterecord) as [Month]
, year(DateRecord) as [Year]
, count(distinct DateRecord ) as day_count
, count(distinct dateadd(day, datediff(day,0, DateRecord ), 0)) as daytime_count
from your_table
where ( DateRecord >= '20130201' and DateRecord < '20130301' )
group by
datename(month,daterecord)
, year(DateRecord)
note the column [daytime_count] is only required if the field [DateRecord] has times othe than 12:00 AM (i.e. it "trims off" times so you deal with dates at 12:AM)
Regarding date range selections: many people will feel that using 'between' is the solution however that isn't true and the safest most reliable method is as I shown above. Note that the higher date is 1st March, but we are asking for information that is less than the 1st March, so we don't need to worry about leap years and we don't have to worry about hours and minutes either.
see: What do BETWEEN and the devil have in common?
try this...
declare #date2 nvarchar(max)
set #date2 = (select getdate())
select DateDiff(Day,#date2,DateAdd(month,1,#date2))
I should use a stored procedure to fill a calendar for 10 years in a SQL database!
Three columns should I fill in with the records: date,weekday or weekend, dayname( monday,...).
Can somebody help me?
You can do it dynamically like this:
WITH Dates AS (
SELECT CONVERT(DATE, getdate()) as [Date]
UNION ALL
SELECT DATEADD(DAY, 1, [Date])
FROM Dates
where Date < dateadd(yy, 10, getdate())
)
SELECT [Date]
FROM Dates
OPTION (MAXRECURSION 4000)
try this version (floors the datetime and adds all requested columns):
;WITH Dates AS (
SELECT DATEADD(day,DATEDIFF(day,0,GETDATE()),0) as DateOf,
CASE WHEN datename(weekday,getdate()) IN ('Saturday','Sunday') THEN 'Weekend'
ELSE 'WeekDay'
END DayType,
datename(weekday,getdate()) DayOfWeekName
UNION ALL
SELECT DateOf+1,
CASE WHEN datename(weekday,DateOf+1) IN ('Saturday','Sunday') THEN 'Weekend'
ELSE 'WeekDay'
END DayType,
datename(weekday,DateOf+1) DayOfWeekName
FROM Dates
where DateOf < dateadd(yy, 10, getdate())
)
SELECT DateOf,DayType,DayOfWeekName
FROM Dates
OPTION (MAXRECURSION 4000)
to insert into a table try this:
DECLARE #DateTable table (DateOf datetime, DayType char(7), DayOfWeekName varchar(10))
;WITH Dates AS (
SELECT DATEADD(day,DATEDIFF(day,0,GETDATE()),0) as DateOf,
CASE WHEN datename(weekday,getdate()) IN ('Saturday','Sunday') THEN 'Weekend'
ELSE 'WeekDay'
END DayType,
datename(weekday,getdate()) DayOfWeekName
UNION ALL
SELECT DateOf+1,
CASE WHEN datename(weekday,DateOf+1) IN ('Saturday','Sunday') THEN 'Weekend'
ELSE 'WeekDay'
END DayType,
datename(weekday,DateOf+1) DayOfWeekName
FROM Dates
where DateOf < dateadd(yy, 10, getdate())
)
INSERT INTO #DateTable (DateOf,DayType,DayOfWeekName)
SELECT DateOf,DayType,DayOfWeekName
FROM Dates
OPTION (MAXRECURSION 4000)
select top 10 * from #DateTable
OTUPUT:
DateOf DayType DayOfWeekName
----------------------- ------- -------------
2011-05-16 00:00:00.000 WeekDay Monday
2011-05-17 00:00:00.000 WeekDay Tuesday
2011-05-18 00:00:00.000 WeekDay Wednesday
2011-05-19 00:00:00.000 WeekDay Thursday
2011-05-20 00:00:00.000 WeekDay Friday
2011-05-21 00:00:00.000 Weekend Saturday
2011-05-22 00:00:00.000 Weekend Sunday
2011-05-23 00:00:00.000 WeekDay Monday
2011-05-24 00:00:00.000 WeekDay Tuesday
2011-05-25 00:00:00.000 WeekDay Wednesday
(10 row(s) affected)
I apporached this as a tally table problem. I am using spt_values from Master as my tally table. It only goes up to 2048 which is enough data for 5.5 years. You can create your own tally table with as many numbers as you need.
Declare #startDate Date = '1/1/2011';
SELECT DateAdd(d, number, #startDate) [Date],
CASE WHEN DATEPART(dw, DateAdd(d, number, #startDate)) IN (1,7) THEN 'Weekend' ELSE 'Weekday' END [WeekDayEnd],
DateName(weekday, DateAdd(d, number, #startDate)) DayOfWeek
FROM spt_values
WHERE type = 'P';
This gets the following results:
Date WeekDayEnd DayOfWeek
2011-01-01 Weekend Saturday
2011-01-02 Weekend Sunday
2011-01-03 Weekday Monday
2011-01-04 Weekday Tuesday
2011-01-05 Weekday Wednesday
2011-01-06 Weekday Thursday
2011-01-07 Weekday Friday
2011-01-08 Weekend Saturday
2011-01-09 Weekend Sunday
2011-01-10 Weekday Monday
USE THIS
set nocount on
SET DATEFIRST 7;
go
select date,
datename(dw,datepart(dw,date)) Day,
datepart(dw,date) Day,
'Segment' = case
when datepart(dw,date)in (5,6) then 'WEEK_END' else 'Week_day' end
from calenderdate
set nocount off
> set nocount on
>
> select date,
> substring(cast(datename(dw,datepart(dw,date))as
> varchar(10)),1,3) Day, 'Segment' =
> case when datepart(dw,date)in (5,6)
> then 'WEEK_END' else 'Week_day' end
> from calenderdate
>
> set nocount off
output
1/1/2011 Monday 7 Week_day
1/2/2011 Tuesday 1 Week_day
I need to split these dates by FY and get the number of months between the dates. I have the FY split answered previously at How to duplicate Rows with new entries link.
Having the following dates:-
ID Start dt End dt
2550 10/1/2010 9/30/2011
2551 8/1/2014 7/31/2015
2552 6/1/2013 5/31/2015
2553 5/10/2012 6/11/2014
I would like the following result set:
ID FY # of Months Start Dt End Dt
2550 2011 12 10/1/2010 9/30/2011
2551 2014 2 8/1/2014 9/30/2014
2551 2015 10 10/1/2014 7/31/2015
2552 2013 4 6/1/2013 9/30/2013
2552 2014 12 10/1/2013 9/30/2014
2552 2015 8 10/1/2014 5/31/2015
2553 2012 5 5/10/2012 9/30/2012
2553 2013 12 10/1/2012 9/30/2013
2553 2014 9 10/1/2013 6/11/2014
An FY is considered from Oct to Sept of the next year.
Thanks in advance!
It was a fun query to build:
declare #t table(id int, start_dt datetime, end_dt datetime)
Insert into #t(id, start_dt, end_dt) Values
(2550, '2010/10/1', '2011/9/30')
, (2551, '2014/8/1', '2015/7/31')
, (2552, '2013/6/1', '2015/5/31')
, (2553, '2012/5/10', '2014/6/11')
, (2554, '2012/5/10', '2012/11/1')
, (2555, '2012/5/10', '2012/8/11')
; with data as(
Select id, start_dt, end_dt, next_y = DATEADD(month, 9, DATEADD(year, DATEDIFF(year, 0, DATEADD(month, 3, start_dt)), 0))
From #t
), split as (
Select id, start_dt
, end_dt = case when next_y > end_dt then end_dt else DATEADD(day, -1, next_y) end
, next_y = DATEADD(year, 1, next_y)
From data as d
Union All
Select s.id
, DATEADD(day, 1, s.end_dt)
, case when DATEADD(day, -1, next_y) < t.end_dt then DATEADD(day, -1, next_y) else t.end_dt end
, next_y = DATEADD(year, 1, s.next_y)
From split as s
Inner Join #t as t on t.id = s.id
Where s.end_dt < t.end_dt
)
Select ID, FY = year(end_dt), '# of Months' = DATEDIFF(month, start_dt, end_dt)+1, 'Start Dt' = start_dt, 'End Dt' = end_dt
From split
Order by id, start_dt, end_dt