2015-12-17 50 views
1

我有以下給出:查詢時間段分割成更小的段

1-僱員時間表

declare @table table (EmployeeId int,StartOn datetime,EndOn datetime) 
insert into @table(EmployeeId,StartOn,EndOn) 
values(1,'15 Dec 2015 09:00','15 Dec 2015 17:00') 

2-歇時間採取

declare @break table(EmployeeId int,StartOn datetime,EndOn datetime) 
insert into @break(EmployeeId,StartOn,EndOn) 
values(1,'15 Dec 2015 09:45','15 Dec 2015 10:10') 
insert into @break(EmployeeId,StartOn,EndOn) 
values(1,'15 Dec 2015 11:30','15 Dec 2015 12:00') 

3 - 的出來應來作爲

EmployeeId Date   From  To  Type 
1   '15 Dec 2015' '09:00' '09:45' 'F' -- Working 
1   '15 Dec 2015' '09:45' '09:10' 'B' -- Break 
1   '15 Dec 2015' '10:10' '11:30' 'F' -- Working 
1   '15 Dec 2015' '11:30' '12:00' 'B' -- Break 
1   '15 Dec 2015' '12:00' '17:00' 'F' -- Working 

c urrently我能夠得到這個結果作爲遊標,我想提高性能,並將結果從光標轉換爲查詢。

任何幫助,將不勝感激。

+1

唉,我認爲解決的辦法要麼需要累計總和,'滯後()',或遞歸CTE。在SQL Server 2008中,這些都不會比基於遊標的解決方案快得多。 –

+0

@GordonLinoff我編輯了標籤,我想看看如何使用滯後或遞歸cte來完成,現在我正試圖用遞歸來解決它 – Monah

回答

2

我想這會做到這一點:

;WITH BreaksPlusEndOfDay AS (
    SELECT * FROM @break 
    UNION 
    SELECT EmployeeId, EndOn, NULL FROM @table --Needed to get the final "F" 
), BreakInfo AS (
    --Find the previous break end for each break (+ end of day) so that we have all the 
    --info to generate both the break record and the previous working record 
    SELECT CurrentBreak.EmployeeId, CurrentBreak.StartOn, CurrentBreak.EndOn, 
     (SELECT MAX(PreviousBreaks.EndOn) AS PreviousBreak_EndOn 
     FROM @break PreviousBreaks 
     WHERE CurrentBreak.EmployeeId = PreviousBreaks.EmployeeId 
     AND CAST(CurrentBreak.StartOn as date) = CAST(PreviousBreaks.StartOn as date) 
     AND CurrentBreak.StartOn > PreviousBreaks.StartOn 
     ) AS PreviousBreak_EndOn 
    FROM BreaksPlusEndOfDay CurrentBreak 
), 
WorkOrBreak AS (SELECT 'F' AS FB UNION SELECT 'B' AS FB) --Virtual table with 2 recs 
SELECT BreakInfo.EmployeeId, 
    CAST(BreakInfo.StartOn as date), 
    CAST(CASE WHEN FB = 'F' THEN ISNULL(PreviousBreak_EndOn, T.StartOn) ELSE BreakInfo.StartOn END AS time) AS StartOn, 
    CAST(CASE WHEN FB = 'F' THEN BreakInfo.StartOn ELSE BreakInfo.EndOn END AS time) AS EndOn, 
    FB 
FROM BreakInfo 
JOIN @table AS T ON BreakInfo.EmployeeId = T.EmployeeID AND CAST(BreakInfo.StartOn AS date) = CAST(T.StartOn AS date) 
CROSS JOIN WorkOrBreak --Generate 2 recs for each rec in BreakInfo 
WHERE FB = 'F' OR BreakInfo.EndOn IS NOT NULL --Remove the "end of day" break 
ORDER BY 1, 2, 3 

應全面,雖然測試它,也表現。我不知道這是否會比你的光標更快。

+0

謝謝你,我爲你戴上帽子 – Monah

+0

剛注意到我忘記了從先前的嘗試中刪除GROUP BY CurrentBreak.StartOn,CurrentBreak.EndOn,CurrentBreak.EmployeeId,將編輯我的答案。 –

1

所推薦的戈登·利諾夫

在這裏我得到了什麼

;with counting as (
    select EmployeeId,count(*) as Number 
    from @break 
    group by EmployeeId 
), combine as (
    select t.EmployeeId,t.StartOn,t.EndOn,b.StartOn as TimeStart,b.EndOn as TimeEnd,c.Number, lead(b.StartOn,1,t.EndOn) over(order by b.StartOn) as Next 
     , lag(b.EndOn,1,t.StartOn) over(order by b.StartOn) as Previous,Duration 
    from @table t 
    inner join @break b on b.EmployeeId = t.EmployeeId 
    inner join counting c on c.EmployeeId=b.EmployeeId 
), sorting as(
    select EmployeeId,TimeStart,TimeEnd,Duration,Number,Next,Previous,row_number() over(order by TimeStart) as [Row] 
    from combine 
), query as(
    select EmployeeId, 
     TimeStart as StartOn, 
     TimeEnd as EndOn, 'A' as Type,Duration 
    from sorting 
    union all 
    select EmployeeId, 
     Previous, 
     TimeStart, 'F' as Type,Duration 
    from sorting 
    union all 
    select EmployeeId, 
     TimeEnd, 
     [Next], 'F' as Type,Duration 
    from sorting 
    where [Row]=Number 
) 
select * from query order by StartOn 

希望這將有助於其他開發人員

+1

如果@break表中沒有記錄,結果如何?我認爲它需要一些改進 – user5135401