How to get 1st and 3rd Saturday and all Sunday between 2 dates using sql - sql

Given a date range, I'd like to return all of the Saturdays and Sundays that fall within that range, with these conditions:
Include Saturdays only if their ordinal position is either the 1st or 3rd Saturday within the month they fall (not within the entire range).
Include all Sundays, along with the ordinal position of that Sunday within the month it falls.
So for example, if the start date is Aug 15, 2021 and the end date is Sep 20, 2021, the output would be:
Dates Saturday Number (in its own month)
---------- ---------------
2021-08-21 3
2021-09-04 1
2021-09-18 3
Dates Sunday Number (in its own month)
---------- ---------------
2021-08-15 3
2021-08-22 4
2021-08-29 5
2021-09-05 1
2021-09-12 2
2021-09-19 3
Then I can take the date range in total (37 days), and subtract the Sundays (6), and the 1st and 3rd Saturdays from each month (3), to end at 28.
Tried this query
DECLARE #sd DATETIME = '2021-08-15' DECLARE #ed DATETIME =
'2021-09-20'
--find first saturday WHILE DATEPART(dw, #sd)<>7 BEGIN SET #sd = DATEADD(dd,1,#sd) END
--get next saturdays ;WITH Saturdays AS (
--initial value SELECT #sd AS MyDate, 1 AS SatNo UNION ALL
--recursive part SELECT DATEADD(dd,7,MyDate) AS MyDate, CASE WHEN SatNo + 1 =6 THEN 1 ELSE SatNo+1 END AS SatNo FROM Saturdays
WHERE DATEADD(dd,7,MyDate)<=#ed
) SELECT * FROM Saturdays WHERE SatNo IN (1,3) OPTION(MAXRECURSION 0)
it does not work properly.
Also Tried this solution
Get number of weekends between two dates in SQL
for calculate week days, but I want only 1st and 3 Saturday and all Sundays

Get a calendar table; it makes this type of business problem a breeze. Here's a simpler one:
CREATE TABLE dbo.Calendar
(
TheDate date PRIMARY KEY,
WeekdayName AS (CONVERT(varchar(8), DATENAME(WEEKDAY, TheDate))),
WeekdayInstanceInMonth tinyint
);
;WITH x(d) AS -- populate with 2020 -> 2029
(
SELECT CONVERT(date, '20200101')
UNION ALL
SELECT DATEADD(DAY, 1, d)
FROM x
WHERE d < '20291231'
)
INSERT dbo.Calendar(TheDate)
SELECT d FROM x
OPTION (MAXRECURSION 0);
;WITH c AS
(
SELECT *, rn = ROW_NUMBER() OVER
(PARTITION BY YEAR(TheDate), MONTH(TheDate), WeekdayName
ORDER BY TheDate)
FROM dbo.Calendar
)
UPDATE c SET WeekdayInstanceInMonth = rn;
Now your query is easy:
DECLARE #start date = '20210815', #end date = '20210920';
SELECT Dates = TheDate,
[Saturday Number] = WeekdayInstanceInMonth
FROM dbo.Calendar
WHERE TheDate >= #start
AND TheDate <= #end
AND WeekdayName = 'Saturday'
AND WeekdayInstanceInMonth IN (1,3);
SELECT Dates = TheDate,
[Sunday Number] = WeekdayInstanceInMonth
FROM dbo.Calendar
WHERE TheDate >= #start
AND TheDate <= #end
AND WeekdayName = 'Sunday';
Results (db<>fiddle example here):
Dates Saturday Number
---------- ---------------
2021-08-21 3
2021-09-04 1
2021-09-18 3
Dates Sunday Number
---------- ---------------
2021-08-15 3
2021-08-22 4
2021-08-29 5
2021-09-05 1
2021-09-12 2
2021-09-19 3
And to get just the number 28:
DECLARE #start date = '20210815', #end date = '20210920';
SELECT DATEDIFF(DAY, #start, #end) + 1
-
(SELECT COUNT(*)
FROM dbo.Calendar
WHERE TheDate >= #start
AND TheDate <= #end
AND WeekdayName = 'Saturday'
AND WeekdayInstanceInMonth IN (1,3))
-
(SELECT COUNT(*)
FROM dbo.Calendar
WHERE TheDate >= #start
AND TheDate <= #end
AND WeekdayName = 'Sunday');

Assuming datefirst is set for Sunday:
(
day(dt) +
datepart(weekday, dateadd(dt, 1 - day(dt))) % 7
) / 7 as SaturdayNumber,
(
day(dt) - 1 +
datepart(weekday, dateadd(dt, 1 - day(dt)))
) / 7 as SundayNumber
For all dates this essentially computes a week number (0-5) within the month, relative to Saturday/Sunday.

Related

Get first day of week T-SQL

How get first day of week (Monday) where week = 6 and year = 2020 I need get 10.02.2020
eg. week 1 in 2020 is date from 06.01.2020 - 12.01.2020
week 6 in 2020 is date from 10.02.2020 - 16.02.2020
DECLARE #YEAR int = 2020;
DECLARE #WEEKSTOADD int = 6;
SET DATEFIRST 1;
SELECT
DATEADD(day,
1 - DATEPART(dw,DATEADD(week,#WEEKSTOADD,cast(cast(#YEAR as varchar(4)) + '0101' as date))),
DATEADD(week,#WEEKSTOADD,cast(cast(#YEAR as varchar(4)) + '0101' as date)))
The following code will get the date of Monday in the week of a given date regardless of the setting of DateFirst or Language:
Cast( DateAdd( day, - ( ##DateFirst + DatePart( weekday, Datum ) - 2 ) % 7, Datum ) as Date )
An example with sample data:
with SampleData as (
select GetDate() - 30 as Datum
union all
select DateAdd( day, 1, Datum )
from SampleData
where Datum < GetDate() )
select Datum,
-- 1 = Monday through 7 = Sunday.
( ##DateFirst + DatePart( weekday, Datum ) - 2 ) % 7 + 1 as WeekDay,
-- Date of Monday in the week of the supplied date.
Cast( DateAdd( day, - ( ##DateFirst + DatePart( weekday, Datum ) - 2 ) % 7, Datum ) as Date ) as Monday
from SampleData;
As per sample data you need substring() :
select t.*, substring(datnum, charindex(' ', datnum) + 1, 10) as dt
from table t
where t.week = 6;

Totals by Day of week over a 2 week period for 1 year

Adding the query (so far) and the result....
DECLARE #var_StartDate DATETIME
DECLARE #var_EndDate DATETIME
SET #var_StartDate = '11/25/2017' -- Set the start date
SET #var_EndDate = '11/23/2018'; --Set the end date
SELECT
DATENAME(DW, VTOTALS.APPLYDATE) AS Day,
FORMAT(SUM(TRANSACTIONS), '#,0.') AS Transactions
FROM
VTOTALS
WHERE
APPLYDATE BETWEEN #var_StartDate AND #var_EndDate
GROUP BY
DATENAME(DW, VTOTALS.APPLYDATE)
ORDER BY
CASE
WHEN DATENAME (DW, VTOTALS.APPLYDATE) = 'Sunday' THEN 1
WHEN DATENAME (DW, VTOTALS.APPLYDATE) = 'Monday' THEN 2
WHEN DATENAME (DW, VTOTALS.APPLYDATE) = 'Tuesday' THEN 3
WHEN DATENAME (DW, VTOTALS.APPLYDATE) = 'Wednesday' THEN 4
WHEN DATENAME (DW, VTOTALS.APPLYDATE) = 'Thursday' THEN 5
WHEN DATENAME (DW, VTOTALS.APPLYDATE) = 'Friday' THEN 6
WHEN DATENAME (DW, VTOTALS.APPLYDATE) = 'Saturday' THEN 7
END ASC
Output:
Day Transactions
Sunday 10,697,804
Monday 25,222,976
Tuesday 25,001,258
Wednesday 24,986,845
Thursday 25,039,354
Friday 24,591,728
Saturday 12,087,903
I am looking for transaction totals by day of week over each day of a pay period for a 1 year timeframe. I can get the DOW but want to split into DOW for week 1 and week 2 over the year between 11/25/17 - 11/23/18
Week 1
Sun 1,980
Mon 38,987
Tue 28,132
Wed 30,879
Thu 30,769
Fri 39,902
Sat 41,912
Week 2
Sun 2,280
Mon 37,987
Tue 29,132
Wed 36,879
Thu 39,769
Fri 42,902
Sat 44,912
Any thoughts?
Thanks
You want weeks that start on Sunday, so you can use the mode argument to week. If you just want the values for one year:
select week(dte, 0), sum(total)
from t
where dte >= '2017-01-01' and dte < '2018-01-01' -- or however you define "year"
group by week(dte, 0);
Take the number of days since some given date, and calculate X % 14. This divides the result by 14 and returns the remainder which will be a number in the range 0 through 13. If you want your result to start on a Sunday, then make sure the given date is a Sunday.
create table VTOTALS
(APPLYDATE date,
[TRANSACTIONS] money)
go
set nocount on
declare #Y int = 0
while #Y < 10000
begin
insert into VTOTALS(ApplyDate,Transactions)
values(
DateAdd(day, #Y % 1000,
'2017-01-01T00:00:00.000' ),
(#Y + 314159)%271828 )
set #Y=#Y+1
end
go
DECLARE #var_StartDate DATETIME
DECLARE #var_EndDate DATETIME
SET #var_StartDate = '2017-11-25T00:00:00.000' -- Set the start date
SET #var_EndDate = '2018-11-23T00:00:00.000'; -- Set the end date
with VWeeks as
( select
DateDiff(day,
'1899-12-31T00:00:00.000',
ApplyDate)%14
as ZeroToThirteen,
* from VTOTALS
WHERE
APPLYDATE BETWEEN #var_StartDate AND #var_EndDate )
SELECT
ZeroToThirteen / 7 + 1 as WeekNum,
DateName ( DW, DateAdd ( day,
ZeroToThirteen,
'1899-12-31T00:00:00.000' ) ) as DayName,
FORMAT(SUM(TRANSACTIONS), '#,0.') AS Transactions
FROM
VWeeks
GROUP BY
ZeroToThirteen
ORDER BY
ZeroToThirteen
returns
WeekNum DayName Transactions
----------- ------------------------------ ----------------
1 Sunday 12,307,100
1 Monday 12,307,360
1 Tuesday 12,307,620
1 Wednesday 12,307,880
1 Thursday 12,308,140
1 Friday 12,308,400
1 Saturday 12,308,660
2 Sunday 12,308,920
2 Monday 12,309,180
2 Tuesday 12,309,440
2 Wednesday 12,309,700
2 Thursday 12,309,960
2 Friday 12,310,220
2 Saturday 12,306,840

Generate list of dates based on the day and occurrence of the month

I want to generate based on the day of the week and number of the occurrence in the month of a date, a list of dates for each month between two dates. Assuming I have a #StartDate = 2016/04/01 and #EndDate = 2016/09/01, i check that #StartDate is on a first Friday of April, then to #EndDate will create dates for all first Friday of each month:
2016/05/06
2016/06/03
2016/07/01
2016/08/05
In case #StartDate = 2016/04/12 and #EndDate = 2016/09/01, I note that the #StartDate is the second Tuesday of April, then went to get every second Tuesday Tuesday of each month :
2016/05/10
2016/06/14
2016/07/12
2016/08/09
In case#StartDate = 2016/04/28 and #EndDate = 2016/09/01, I note that the #StartDate is on the last Thursday of the month of April:
2016/05/26
2016/06/30
2016/07/28
2016/08/25
In the last case, i need to verify the number of weeks of each month, because exists months only with 4 weeks or with 5 weeks and i want the last occurrence.
What I have done? I found a code that gives me every Monday in the third week of the month, and i adopted a little to get a #StartDate and #EndDate:
;with
filler as (select row_number() over (order by a) a from (select top 100 1 as a from syscolumns) a cross join (select top 100 1 as b from syscolumns) b),
dates as (select dateadd(month, a-1, #StartDate ) date from filler where a <= 1000 and dateadd(month, a-1, #StartDate) < #EndDate),
FirstMonday as (
select dateadd(day, case datepart(weekday,Date) /*this is the case where verify the week day*/
when 1 then 1
when 2 then 0
when 3 then 6
when 4 then 5
when 5 then 4
when 6 then 3
when 7 then 2
end, Date) as Date
,case when datepart(weekday, #StartDate) = 1 then 3 else 2 end as Weeks /*here i verify the number of weeks to sum in the next date*/
from dates
)
select dateadd(week, Weeks, Date) as ThirdMonday
from FirstMonday
So, it is:
set #NumSemana = datepart(day, datediff(day, DATEADD(mm, DATEDIFF(mm,0,#StartDate), 0), #StartDate)/7 * 7)/7 + 1;
WITH AllDays
AS ( SELECT #StartDate AS [Date], DATEPART(month, #StartDate) as validMonth
UNION ALL
SELECT DATEADD(week, 1, [Date]),
iif(DATEPART(month,DATEADD(week, 1, [Date])) < validMonth + #PeriodicityRepeat, validMonth, validMonth + #PeriodicityRepeat)
FROM AllDays
WHERE
DATEPART(month,[Date]) <= DATEPART(month,#EndDate)
and DATEPART(year,[Date]) <= DATEPART(year,#EndDate)
),
rankedDays
AS(
SELECT [Date], validMonth,
row_number() over ( partition by DATEPART( month, [Date]) order by [Date]) ascOrder,
row_number() over ( partition by DATEPART( month, [Date]) order by [Date] desc) descOrder
FROM AllDays
WHERE DATEPART(month, [Date]) = validMonth
)
select [Date]
from rankedDays
where ((ascOrder = #NumSemana and #NumSemana <=4 )
or (descOrder = 1 and #NumSemana = 5)
or [Date] = #StartDate )
and [Date] < #EndDate
OPTION (MAXRECURSION 0)
Query:
DECLARE #StartDate DATE = '2016-04-28',
#EndDate DATE = '2016-09-01'
;WITH dates AS (
SELECT DATEADD(week, -5, #StartDate) as date_
UNION ALL
SELECT DATEADD(week,1,date_)
FROM dates
WHERE DATEADD(week,1,date_) < #enddate
), final AS (
SELECT ROW_NUMBER() OVER (PARTITION BY DATEPART(year,date_), DATEPART(month,date_) ORDER BY date_ ASC) as RN,
date_
FROM dates
), weeks AS (
SELECT *
FROM (VALUES
(1,1),
(2,2),
(3,3),
(4,4),
(4,5),
(5,4),
(5,5)
) as t(w1,w2)
WHERE w1 = (SELECT RN FROM final WHERE date_ = #StartDate)
)
SELECT MAX(date_) as date_
FROM final f
INNER JOIN weeks w ON f.RN = w.w2
WHERE date_ between #StartDate and #EndDate AND date_ != #StartDate
GROUP BY DATEPART(YEAR,date_), DATEPART(MONTH,date_)
ORDER BY MAX(date_) ASC
Outputs:
For #StartDate = 2016/04/01 and #EndDate = 2016/09/01
date_
----------
2016-05-06
2016-06-03
2016-07-01
2016-08-05
(4 row(s) affected)
For #StartDate = 2016/04/12 and #EndDate = 2016/09/01
date_
----------
2016-05-10
2016-06-14
2016-07-12
2016-08-09
(4 row(s) affected)
For #StartDate = 2016/04/28 and #EndDate = 2016/09/01
date_
----------
2016-05-26
2016-06-30
2016-07-28
2016-08-25
(4 row(s) affected)

Working days from a given date

User will select a date in frontend and flexibledays, say for example if they have selected '2014-07-17' as date and flexibledays as 2, then we need to display both 2 previous and next 2 working days as like below,
2014-07-15
2014-07-16
2014-07-17
2014-07-20
2014-07-21
excluding weekends (friday and saturday), for use weekends is friday and saturday.
I have used the below query
DECLARE #MinDate DATE, #MaxDate DATE;
SELECT #MinDate = DATEADD(Day, -#inyDays ,#dtDate), #MaxDate = DATEADD(Day,#inyDays ,#dtDate)
DECLARE #DayExclusionValue VARCHAR(20)
SELECT #DayExclusionValue = dbo.UDF_GetConfigSettingValue('DaysToExclude')
DECLARE #NumOfWeekends INT
SELECT #NumOfWeekends= (DATEDIFF(wk, #MinDate, #MaxDate) * 2) +(CASE WHEN DATENAME(dw, #MinDate) = 'Friday' THEN 1 ELSE 0 END) +(CASE WHEN DATENAME(dw, #MaxDate) = 'Saturday' THEN 1 ELSE 0 END)
SET #MaxDate = DATEADD(Day,#inyDays + #NumOfWeekends ,#dtDate)
;WITH CalculatedDates AS
(
SELECT dates = #MinDate
UNION ALL
SELECT DATEADD(day, 1, dates)
FROM CalculatedDates
WHERE DATEADD(day, 1, dates) <= #MaxDate
)
SELECT dates FROM CalculatedDates
WHERE dates >= CAST(GETDATE() AS DATE)
AND DATENAME(DW, dates) NOT IN (SELECT Value FROM UDF_GetTableFromString(#DayExclusionValue))
OPTION (MAXRECURSION 0);
but the above query is not working properly.
Can you pls suggest me any other solution.
This example will work for Oracle, you did not say what DB you were using. If you have a list of vacations you need to join that in as indicated. It would need to be a outerjoin, and you need to add a case or something so that the vacation tables 'exclude' days override the generated days.
Also I chose a multiplier on random. When only dealing with weekend 8 was more than enough, but if your vacation table includes a lot of consecutive vacation days it might no longer be.
select d from(
select rownum nn, d, sysdate - d, first_value (rownum) over (order by abs(sysdate-d)) zero_valu
from (
select sysdate+n d, to_char(sysdate+n,'DAY'), CASE to_char(sysdate+n,'D') WHEN '6' THEN 'exclude' WHEN '7' THEN 'exclude' ELSE 'include' END e_or_i from
(SELECT ROWNUM-9 n -- 9=flexibleday*8/2 +1
FROM ( SELECT 1 just_a_column
FROM dual
CONNECT BY LEVEL <= 16 -- 8=flexibleday * 8
)
)
) where e_or_i = 'include' -- in this step you need to join in a table of holidays or such if you need that.
) where abs(nn-7) <= 2 -- 2=flexiday
order by d
DECLARE #StartDate DATE = '2014-07-17';
SELECT *
FROM
(
--Show Closest Previous 2 Days Not In Friday or Saturday
SELECT TOP 2
DATEADD(DAY, -nr, #StartDate) CheckDate,
DATENAME(DW, DATEADD(DAY, -nr, #StartDate)) CheckName,
-nr CheckCount
FROM (VALUES(1),(2),(3),(4)) AS Numbers(nr)
WHERE DATENAME(DW, DATEADD(DAY, -nr, #StartDate)) NOT IN ('Friday','Saturday')
UNION ALL
--Show Todays Date If Not Friday or Saturday
SELECT TOP 1
DATEADD(DAY, +nr, #StartDate) CheckDate,
DATENAME(DW, DATEADD(DAY, +nr, #StartDate)) CheckName,
nr CheckCount
FROM (VALUES(0)) AS Numbers(nr)
WHERE DATENAME(DW, DATEADD(DAY, +nr, #StartDate)) NOT IN ('Friday','Saturday')
UNION ALL
--Show Closest Next 2 Days Not In Friday or Saturday
SELECT TOP 2
DATEADD(DAY, +nr, #StartDate) CheckDate,
DATENAME(DW, DATEADD(DAY, +nr, #StartDate)) CheckName,
nr CheckCount
FROM (VALUES(1),(2),(3),(4)) AS Numbers(nr)
WHERE DATENAME(DW, DATEADD(DAY, +nr, #StartDate)) NOT IN ('Friday','Saturday')
) d
ORDER BY d.CheckDate
I break it into 3 parts, previous 2 days, today (if applicable) and next 2 days
Here is the output:
CheckDate CheckName CheckCount
2014-07-15 Tuesday -2
2014-07-16 Wednesday -1
2014-07-17 Thursday 0
2014-07-20 Sunday 3
2014-07-21 Monday 4
I use the datename since not sure what ##datefirst your server is set to. the values() section is just a numbers table (you should create a numbers table as big as the amount of records you want to return plus any weekends you are crossing over) and then the TOP 2 in the first and last sections would be replaced with the number of days you wanted to return before and after.
**** Update with generic numbers table functionality added:
Here we declare the starting date and the number of previous and next days we would like to pull:
DECLARE #StartDate DATE = '2014-07-20';
DECLARE #MaxBusDays INT = 5
This next section creates a numbers table (can be easily found via google)
DECLARE #number_of_numbers INT = 100000;
;WITH
a AS (SELECT 1 AS i UNION ALL SELECT 1),
b AS (SELECT 1 AS i FROM a AS x, a AS y),
c AS (SELECT 1 AS i FROM b AS x, b AS y),
d AS (SELECT 1 AS i FROM c AS x, c AS y),
e AS (SELECT 1 AS i FROM d AS x, d AS y),
f AS (SELECT 1 AS i FROM e AS x, e AS y),
numbers AS
(
SELECT TOP(#number_of_numbers)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS number
FROM f
)
Now we use the numbers table and a row_number setting to pull only the number of rows before and after (plus the date of, if it's not fri/sat as wanted) that are working days (not fri/sat)
SELECT *
FROM
(
--Show Closest Previous x Working Days (Not Friday or Saturday)
SELECT * FROM
(
SELECT
DATEADD(DAY, -number, #StartDate) CheckDate,
DATENAME(DW, DATEADD(DAY, -number, #StartDate)) CheckName,
-number CheckCount,
ROW_NUMBER() OVER (ORDER BY number ASC) AS RowCounter
FROM Numbers
WHERE DATENAME(DW, DATEADD(DAY, -number, #StartDate)) NOT IN ('Friday','Saturday')
) a
WHERE a.RowCounter <= #MaxBusDays
UNION ALL
--Show Todays Date If Working Day (Not Friday or Saturday)
SELECT TOP 1
#StartDate CheckDate,
DATENAME(DW, #StartDate) CheckName,
0 CheckCount,
0 RowCounter
WHERE DATENAME(DW, #StartDate) NOT IN ('Friday','Saturday')
UNION ALL
--Show Closest Next x Working Days (Not Friday or Saturday)
SELECT * FROM
(
SELECT
DATEADD(DAY, +number, #StartDate) CheckDate,
DATENAME(DW, DATEADD(DAY, +number, #StartDate)) CheckName,
number CheckCount,
ROW_NUMBER() OVER (ORDER BY number ASC) AS RowCounter
FROM Numbers
WHERE DATENAME(DW, DATEADD(DAY, +number, #StartDate)) NOT IN ('Friday','Saturday')
) b
WHERE b.RowCounter <= #MaxBusDays
) c
ORDER BY c.CheckDate
Here is the output: (2014-07-20 is the middle row)
CheckDate CheckName CheckCount RowCounter
2014-07-13 Sunday -7 5
2014-07-14 Monday -6 4
2014-07-15 Tuesday -5 3
2014-07-16 Wednesday -4 2
2014-07-17 Thursday -3 1
2014-07-20 Sunday 0 0
2014-07-21 Monday 1 1
2014-07-22 Tuesday 2 2
2014-07-23 Wednesday 3 3
2014-07-24 Thursday 4 4
2014-07-27 Sunday 7 5

Need Sql query output like this format

I have one time sheet table when i fetch the records it will display like this for one week.
This record is from one week from June 10 to June 16
EmpId Monday Tuesday Wednesday Thursday Friday Saturday Sunday StartDate EndDate
1 08:00 08:12 00:00 04:00 00:00 03:00 00:00 05/10/2013 05/16/2013
Need the output like this
Empid Monday startdate EndDate
1 08:00 05/10/2013 05/10/2013
1 08:12 05/11/2013 05/11/2013
1 04:00 05/13/2013 05/13/2013
1 03:00 05/15/2013 05/15/2013
This is basically an unpivot query. Because of the time fields, this version chooses to do it explicitly (using cross join and case) rather than using unpivot:
select t.*
from (select h.empid,
(case when n = 0 then Monday
when n = 1 then Tuesday
when n = 2 then Wednesday
when n = 3 then Thursday
when n = 4 then Friday
when n = 5 then Saturday
when n = 6 then Sunday
end) as hours,
(startdate + n) as StartDate,
(startdate + n) as EndDate
from hours h join
(select 0 as n union all
select 1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6
) n
) t
where hours > 0;
You can see the SQLFiddle here. And there is no problem running this on larger amounts of data.
This should get you started:
declare #Hours as Table ( EmpId Int, Monday Time, Tuesday Time, Wednesday Time,
Thursday Time, Friday Time, Saturday Time, Sunday Time, StartDate Date, EndDate Date );
insert into #Hours
( EmpId, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday, StartDate, EndDate )
values
( 1, '08:00', '08:12', '00:00', '04:00', '00:00', '03:00', '00:00', '20130510', '20130516' );
select * from #Hours;
with Week as (
-- Build a table of all of the dates in the work week.
select StartDate as WorkDate, EndDate
from #Hours
union all
select DateAdd( day, 1, WorkDate ), EndDate
from Week
where WorkDate < EndDate )
-- Output the result.
select EmpId,
case DatePart( weekday, W.WorkDate )
when 1 then H.Monday
when 2 then H.Tuesday
when 3 then H.Wednesday
when 4 then H.Thursday
when 5 then H.Friday
when 6 then H.Saturday
when 7 then H.Sunday
end as Hours,
WorkDate as StartDate, WorkDate as EndDate,
DatePart( weekday, W.WorkDate ) as DayOfWeek
from Week as W inner join
#Hours as H on H.StartDate <= W.WorkDate and W.WorkDate <= H.EndDate;
with Week as (
-- Build a table of all of the dates in the work week.
select StartDate as WorkDate, EndDate
from #Hours
union all
select DateAdd( day, 1, WorkDate ), EndDate
from Week
where WorkDate < EndDate ),
DaysHours as (
-- Build a table of the hours assigned to each date.
select EmpId,
case DatePart( weekday, W.WorkDate )
when 1 then H.Monday
when 2 then H.Tuesday
when 3 then H.Wednesday
when 4 then H.Thursday
when 5 then H.Friday
when 6 then H.Saturday
when 7 then H.Sunday
end as Hours,
WorkDate as StartDate, WorkDate as EndDate
from Week as W inner join
#Hours as H on H.StartDate <= W.WorkDate and W.WorkDate <= H.EndDate )
-- Output the non-zero hours.
select EmpId, Hours, StartDate, EndDate
from DaysHours
where Hours <> Cast( '00:00' as Time );
This works for a single row, but you will need to make some changes if your dataset grows.