我有以下表结构(myTable):
EmployeeID, StartDate, EndDate, Hours
---------------------------------------
1 1/1/2016 1/8/2016 20
2 1/4/2016 1/6/2016 10
3 1/2/2016 1/3/2016 13
我需要根据开始日期和结束日期的日期来划分小时数并显示每天的记录,如下所示:
1 1/1/2016 1/2/2016 2.85
1 1/2/2016 1/3/2016 2.85
...
1 1/7/2016 1/8/2016 2.85
2 1/4/2016 1/5/2016 5
2 1/5/2016 1/6/2016 5
3 1/2/2016 1/3/2016 13
小时数应四舍五入到小数点后两位。假设开始和结束日期永远不会相同。
如何使用T-SQL执行此操作?
编辑:我不是一个SQL大师,所以我没有尝试过这么做,因为它看起来不像一个简单的选择。我想我需要使用'分区'?几个小时,2.86也没关系。只要它是一致的,向上或向下舍入并不重要。另外,为了澄清,我不需要3行。我需要10排。我不只需要一个简单的
hours / datediff(day, startdate, enddate)
答案 0 :(得分:1)
您必须使用计数表:
;WITH Tally AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n
FROM (VALUES (0), (0), (0), (0), (0), (0)) t1(v)
CROSS JOIN (VALUES (0), (0), (0), (0), (0), (0)) t2(v)
)
SELECT t1.EmployeeID,
DATEADD(d, t3.n-1, t1.StartDate),
DATEADD(d, t3.n, t1.StartDate),
ROUND([Hours] * 1.0 / t2.days, 2)
FROM mytable AS t1
CROSS APPLY (SELECT DATEDIFF(d, t1.StartDate, t1.EndDate)) As t2(days)
JOIN Tally AS t3 ON t3.n <= t2.days
ORDER BY t1.EmployeeID, n, t1.StartDate
上述查询使用CTE
来创建包含36行的计数表。它可以轻松扩展以创建更多行。
答案 1 :(得分:1)
可以通过加入数字列表来实现日期范围的无法实现 并且master..spt_values可用于此。
要将[hours]除以日期,它首先被转换为浮点数,然后通过舍入到2位小数被截断。
select t.EmployeeID,
dateadd(d, v.number, t.StartDate) as StartDate,
dateadd(d, v.number+1, t.StartDate) as EndDate,
t.DivHours as [Hours]
from (
select EmployeeID, StartDate, EndDate,
round(cast([Hours] as float)/datediff(d, StartDate, EndDate),2,1) as DivHours
from myTable
where EndDate > StartDate
) t
join master..spt_values v
on (v.type='P' and v.number >= 0 and v.number < datediff(d, t.StartDate, t.EndDate));
给出:
EmployeeID StartDate EndDate Hours
1 2016-07-01 2016-07-02 2,85
1 2016-07-02 2016-07-03 2,85
1 2016-07-03 2016-07-04 2,85
1 2016-07-04 2016-07-05 2,85
1 2016-07-05 2016-07-06 2,85
1 2016-07-06 2016-07-07 2,85
1 2016-07-07 2016-07-08 2,85
2 2016-07-04 2016-07-05 5
2 2016-07-05 2016-07-06 5
3 2016-07-02 2016-07-03 13
然而,只有当日期低于2047年时才会有效 因为2047是从系统表中获得的最大数量 但是,这仍然是一个超过5年的日期范围。
但是如果你在那张桌子里有更大的范围 然后你就可以生成一个包含更多数字的表格 此示例将1000000个数字放入表变量:
DECLARE @Numbers TABLE (num int primary key);
-- Who dares to claim that cross joins are always useless?
WITH d AS (select n from (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9))q(n))
insert into @Numbers (num)
select (d6.n*100000+d5.n*10000+d4.n*1000+d3.n*100+d2.n*10+d1.n) as num
from d d1, d d2, d d3, d d4, d d5, d d6;
select count(*) as total, min(num) as min_num, max(num) as max_num from @Numbers;
你也可以使用递归。
但是如果你想添加额外的列,那么这种方法会有点痛苦。
要从myTable添加更多列,您可以将myTable连接到EmployeeID上的R.
WITH R (EmployeeID, StartDate, EndDate, FinalDate, [Hours]) AS
(
SELECT EmployeeID, StartDate, dateadd(d, 1, StartDate),
EndDate as FinalDate,
round(cast([Hours] as float)/datediff(d, StartDate, EndDate),2,1)
from myTable
where StartDate < EndDate
UNION ALL
SELECT EmployeeID, dateadd(d, 1, StartDate), dateadd(d, 2, StartDate),
FinalDate, [Hours]
FROM R WHERE dateadd(d, 1, StartDate) < FinalDate
)
SELECT EmployeeID, StartDate, EndDate, [Hours]
FROM R
ORDER BY EmployeeID, StartDate, EndDate;
如果分时的总和仍需要等于原来的小时数?
然后它变得更加复杂。
declare @myTable TABLE (EmployeeID int, StartDate date, EndDate date, [Hours] int);
insert into @myTable values
(0,'2016-1-1','2016-1-4',10),
(1,'2016-1-1','2016-1-8',20);
WITH R (EmployeeID, StartDate, EndDate, FinalDate, [Hours], RemainingHours) AS
(
SELECT EmployeeID,
StartDate,
dateadd(d, 1, StartDate),
EndDate,
round(cast([Hours] as float)/datediff(d, StartDate, EndDate),2,1),
round(cast([Hours] as float),2,1)
from @myTable
where StartDate < EndDate
UNION ALL
SELECT EmployeeID,
dateadd(d, 1, StartDate),
dateadd(d, 1, EndDate),
FinalDate,
(case when dateadd(d, 1, EndDate) < FinalDate then [Hours] else (RemainingHours - [Hours]) end),
(RemainingHours - [Hours])
FROM R WHERE EndDate < FinalDate
)
SELECT EmployeeID, StartDate, EndDate, [Hours]
FROM R
ORDER BY EmployeeID, StartDate, EndDate;