对于每个有数据的行,每个类别都需要一行

时间:2014-09-26 21:14:47

标签: sql sql-server sql-server-2012

我有时间表数据,我需要按日期范围创建报告。我需要为每个人每天排一行,每次都输入一行。如果在给定的一天没有该时间类型的条目,我想要空数据。我尝试过左连接,但似乎没有工作。交叉连接会产生错误的数据。

我拥有的表是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
等...

2 个答案:

答案 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
相关问题