生成一組時間範圍的問題可以通過即席查詢,時間表來解決。
declare @starttime time(0) = '09:00';
declare @endtime time(0) = '15:00';
with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
/* adhoc time range table */
, blocks as (
select top (48)
[block]=convert(time(0),dateadd(minute,30*(row_number() over (order by (select 1)) -1),0))
, [next_block]=convert(time(0),dateadd(minute,30*(row_number() over (order by (select 1))),0))
from n as deka cross join n as hecto
order by row_number() over (order by (select 1))
)
select *
from blocks b
where b.block >= @starttime
and b.block < @endtime;
rextester演示:http://rextester.com/WJZY39264
回報:
+----------+------------+
| block | next_block |
+----------+------------+
| 09:00:00 | 09:30:00 |
| 09:30:00 | 10:00:00 |
| 10:00:00 | 10:30:00 |
| 10:30:00 | 11:00:00 |
| 11:00:00 | 11:30:00 |
| 11:30:00 | 12:00:00 |
| 12:00:00 | 12:30:00 |
| 12:30:00 | 13:00:00 |
| 13:00:00 | 13:30:00 |
| 13:30:00 | 14:00:00 |
| 14:00:00 | 14:30:00 |
| 14:30:00 | 15:00:00 |
+----------+------------+
解碼你的weekdayMask
如果你依賴於星期一是一週的第一天,使用datepart(weekday,...)
可以得到脆(這根據語言設置而改變,並且被set datefirst
覆蓋)。
我的解決辦法是,檢查weekday
的datename()
和從表中拉關聯的值,(這裏我在一個共同的表表達式中使用的表值構造來代替。)
其他條件而定可以應用使用聯接,where
,isnull()
或coalesce()
處理null
值(expiryDate
),子查詢得到一個工作日來比較weekdayMask
,並not exists()
檢查重疊的相關值。
最後一個問題是是否使用過程或表值函數。如果您打算使用表值函數,我強烈建議使用內聯表值函數來獲得顯着的性能改進,而不是使用可比較的多語句表值函數。由於這似乎比一個程序更難處理,所以我選擇了這個答案。
如果你決定你喜歡什麼,重寫一個表值函數作爲一個過程應該很容易。
create function dbo.udf_availability_by_empId_Date (
@empid int
, @date date
) returns table with schemabinding as return (
/* Weekday mask using weekday names
to avoid any issue with datefirst and datepart()
this uses a 7 row lookup table for the bitmask check */
with Mask as (
select weekday_name, weekday_value
from (values ('Monday',1) ,('Tuesday',2) ,('Wednesday',4) ,('Thursday',8)
,('Friday',16) ,('Saturday',32) ,('Sunday',64)) w (weekday_name,weekday_value))
/* adhoc numbers table to generate range in half hour increments */
, n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
/* adhoc time range table */
, blocks as (
select top (48)
[block]=convert(time(0),dateadd(minute,30*(row_number() over (order by (select 1)) -1),0))
, [next_block]=convert(time(0),dateadd(minute,30*(row_number() over (order by (select 1))),0))
from n as deka cross join n as hecto
order by row_number() over (order by (select 1))
)
select b.block
from dbo.employee e
inner join dbo.shifts s
on s.EmpId = e.Empid
inner join blocks b
on b.block >= s.starttime
and b.block < s.endtime
where e.EmpId = @empid
and e.IsActive = 1
and isnull(s.ExpiryDate,'20630405') >= @date
/* get the value to check against the weekday bitmask */
and s.weekdaymask & (
select weekday_value
from Mask
where weekday_name = datename(weekday,@date)
) != 0
/* check for appointments that overlap the block */
and not exists (
select 1
from dbo.appointment a
where a.endtime > b.block
and b.next_block > a.starttime
and a.apptDate = @date
and a.Empid = @empid
)
);
go
如果你想改變返回time
的顯示器,那麼你可以使用convert(varchar(10),a.block,100)
得到一個AM/PM格式的字符串,但我會建議讓你的應用層做了格式化。
爲了您的測試用例:
select
a.block
, AvailableBlockStartingTime = convert(varchar(10),a.block,100)
from dbo.udf_availability_by_empId_Date(1,'20170416') as a
rextester演示:http://rextester.com/TXBUP51531
回報:
+----------+----------------------------+
| block | AvailableBlockStartingTime |
+----------+----------------------------+
| 09:00:00 | 9:00AM |
| 09:30:00 | 9:30AM |
| 11:00:00 | 11:00AM |
| 11:30:00 | 11:30AM |
| 12:00:00 | 12:00PM |
| 12:30:00 | 12:30PM |
| 13:00:00 | 1:00PM |
| 14:00:00 | 2:00PM |
| 14:30:00 | 2:30PM |
+----------+----------------------------+
因爲它是一個表值函數,我們不妨有apply()
一些有趣。
/* show all employee availability for a given date */
select
e.EmpId
, e.FirstName
, e.IsActive
, a.block
from dbo.Employee e
outer apply dbo.udf_availability_by_empId_Date(e.EmpId,'20170416') a
回報:
+-------+----------------------+----------+----------+
| EmpId | FirstName | IsActive | block |
+-------+----------------------+----------+----------+
| 1 | fdmillion | True | 09:00:00 |
| 1 | fdmillion | True | 09:30:00 |
| 1 | fdmillion | True | 11:00:00 |
| 1 | fdmillion | True | 11:30:00 |
| 1 | fdmillion | True | 12:00:00 |
| 1 | fdmillion | True | 12:30:00 |
| 1 | fdmillion | True | 13:00:00 |
| 1 | fdmillion | True | 14:00:00 |
| 1 | fdmillion | True | 14:30:00 |
| 2 | shift expired person | True | NULL |
| 3 | inactive person | False | NULL |
| 4 | other person | True | NULL |
+-------+----------------------+----------+----------+
或檢查的時間表一週僱員:
/* show a weeks worth of availability for a given empid */
declare @empid int = 1
declare @fromdate date = '20170410';
;with n as (select [Date]=dateadd(day,n,@fromdate) from (values(0),(1),(2),(3),(4),(5),(6)) t(n))
select
[Date] = convert(char(10),n.[Date],120)
, Weekday_Name = datename(weekday,n.[Date])
, a.block
from n
outer apply dbo.udf_availability_by_empId_Date(@empid,[Date]) a
order by n.[Date]
回報:
+------------+--------------+----------+
| Date | Weekday_Name | block |
+------------+--------------+----------+
| 2017-04-10 | Monday | NULL |
| 2017-04-11 | Tuesday | NULL |
| 2017-04-12 | Wednesday | NULL |
| 2017-04-13 | Thursday | NULL |
| 2017-04-14 | Friday | 09:00:00 |
| 2017-04-14 | Friday | 09:30:00 |
| 2017-04-14 | Friday | 10:00:00 |
| 2017-04-14 | Friday | 10:30:00 |
| 2017-04-14 | Friday | 11:00:00 |
| 2017-04-14 | Friday | 11:30:00 |
| 2017-04-14 | Friday | 12:00:00 |
| 2017-04-14 | Friday | 12:30:00 |
| 2017-04-14 | Friday | 13:00:00 |
| 2017-04-14 | Friday | 13:30:00 |
| 2017-04-14 | Friday | 14:00:00 |
| 2017-04-14 | Friday | 14:30:00 |
| 2017-04-15 | Saturday | 09:00:00 |
| 2017-04-15 | Saturday | 09:30:00 |
| 2017-04-15 | Saturday | 10:00:00 |
| 2017-04-15 | Saturday | 10:30:00 |
| 2017-04-15 | Saturday | 11:00:00 |
| 2017-04-15 | Saturday | 11:30:00 |
| 2017-04-15 | Saturday | 12:00:00 |
| 2017-04-15 | Saturday | 12:30:00 |
| 2017-04-15 | Saturday | 13:00:00 |
| 2017-04-15 | Saturday | 13:30:00 |
| 2017-04-15 | Saturday | 14:00:00 |
| 2017-04-15 | Saturday | 14:30:00 |
| 2017-04-16 | Sunday | 09:00:00 |
| 2017-04-16 | Sunday | 09:30:00 |
| 2017-04-16 | Sunday | 11:00:00 |
| 2017-04-16 | Sunday | 11:30:00 |
| 2017-04-16 | Sunday | 12:00:00 |
| 2017-04-16 | Sunday | 12:30:00 |
| 2017-04-16 | Sunday | 13:00:00 |
| 2017-04-16 | Sunday | 14:00:00 |
| 2017-04-16 | Sunday | 14:30:00 |
+------------+--------------+----------+
附加說明:time(0)
對於time
(缺省分數精度爲7)是3個字節對5個字節。您可以通過包含0-2的精度來削減每行的4個字節。
參考:
非常全面。我不需要你寫的所有東西,但是我可以從中選擇我需要什麼來生成必要的記錄。謝謝! – fdmillion
@fdmillion樂於助人! – SqlZim