我有时间表数据,我需要按日期范围创建报告。我需要为每个人每天排一行,每次都输入一行。如果在给定的一天没有该时间类型的条目,我想要空数据。我尝试过左连接,但似乎没有工作。交叉连接会产生错误的数据。
我拥有的表是Person
表(personID, Name
),TimeLog
表(TimeLogID, StartDate, EndDate, TimeLogTypeID
)和TimeLogType
表({{1 }})
我可以在结果集中获得的只是包含数据的行,而不是每个TimeLogTypeID, PersonID, Description, DeletedInd
的空行
这是我到目前为止所拥有的:
TimeLogType
数据如下:
TimeLogType:
1,Billable
2,不可计费
人:
1,比利
2,汤姆
TimeLog:
1,1,2015-05-01 08:00:00,2014-05-01 09:00:00,1:0
2,1,2014-05-01 09:00:00,2014-05-01 10:00:00,1:0
3,2,2014-05-01 08:00:00,2014-05-01 08:30:00,2,0
4,2,2014-05-01 08:30:00,2014-05-01 09:00:00,1:0
5,1,2014-05-02 08:00:00,2014-05-02 09:00:00,2,0
预期输出:(按人,日期,时间日志类型排序)
日,人,帐单类型,总时数
2014-05-01,Billy,Billiable,2.0
2014-05-01,Billy,非账单,NULL
2014-05-02,Billy,Billiable,1.0
2014-05-02,比利,非账单,NULL
等等...
2014-05-01,Tom,Billiable,0.5
2014-05-01,汤姆,非法案,0.5
等...
答案 0 :(得分:1)
您需要先生成所有组合,然后使用left join
引入所需的信息。我认为查询是这样的:
with dates as (
select dateadd(day, number - 1, mind) as thedate
from (select min(StartDate) as mind, max(EndDate) as endd
from TimeLogType
) tlt join
master..spt_values v
on dateadd(day, v.number, mind) <= tlt.endd
)
select p.PersonId, tlt.TimeLogTypeId, d.thedate,
from Person p cross join
(select tlt.* from TimeLogType tlt where ISNULL(TimeLogType.DeletedInd, 0) = 0
) tlt cross join
date d left join
TimeLog tl
on tl.Person_id = p.PersonId and tl.TimeLogTypeId = tlt.TimeLogTypeId and
d.thedate >= tl.StartDate and d.thedate <= tl.EndDate
答案 1 :(得分:0)
在阅读戈登的答案之后,这就是我想出的。我分步创建了它,所以我可以看到发生了什么。我用master..spt_values表创建了日期。我还创建了一个临时表,所以我可以只选择那些有TimeLogRecord的表,然后重新使用它来获取最终选择的详细信息。让我知道是否有任何方法可以让它更快地运行。
DECLARE
@startDate DATE,
@endDate DATE
SET @startDate = '2014-01-01'
SET @endDate = '2014-01-31'
-- create day rows --
;WITH dates(TimeLogDay) AS
(
SELECT @startDate AS TimeLogDay
UNION ALL
SELECT DATEADD(d, 1, TimeLogDay)
FROM dates
WHERE TimeLogDay < @enddate
)
-- create a type row for each day --
SELECT
dates.TimeLogDay,
tlt.TimeLogTypeID
INTO #TypeDate
FROM
dates CROSS JOIN
(SELECT
TimeLogType.TimeLogTypeID
FROM
TimeLogType
WHERE
ISNULL(TimeLogType.DeletedInd, 0) = 0
) AS TLT
-- create a temp person table for referance later ---
SELECT * INTO #person FROM Person WHERE Person.personID IN
(SELECT Timelog.PersonID FROM TimeLog WHERE TimeLog.StartDateTime BETWEEN @startDate AND @endDate)
-- sum up the log times and tie in the date/type rows --
SELECT
#TypeDate.TimeLogDay,
#TypeDate.TimeLogTypeID,
#person.PersonID,
SUM(dbo.fnCalculateHoursAsDecimal(TimeLog.StartDateTime, TimeLog.EndDateTime)) AS Hours
INTO #Hours
FROM
#person CROSS JOIN
#TypeDate LEFT JOIN
TimeLog ON
TimeLog.PersonID = #person.PersonID AND
TimeLog.TimeLogTypeID = #TypeDate.TimeLogTypeID AND
#TypeDate.TimeLogDay = CONVERT(DATE, TimeLog.StartDateTime, 101)
GROUP BY
#TypeDate.TimeLogDay,
#TypeDate.TimeLogTypeID,
#person.PersonID
-- now tie in the details to complete --
SELECT
#Hours.TimeLogDay,
TimeLogType.Description,
Person.LastName,
Person.FirstName,
#Hours.Hours
FROM
#Hours LEFT JOIN
Person ON #Hours.PersonID = Person.PersonID LEFT JOIN
TimeLogType ON #Hours.TimeLogTypeID = TimeLogType.TimeLogTypeID
ORDER BY
Person.FirstName,
Person.LastName,
#Hours.TimeLogDay,
TimeLogType.SortOrder