【问题标题】:For every row with data I need a row for each category对于有数据的每一行,每个类别我都需要一行
【发布时间】:2014-09-26 21:14:46
【问题描述】:

我有需要按日期范围创建报告的时间表数据。我需要为每个人每天和每个时间类型设置一行。如果在给定日期没有该时间类型的条目,我想要空数据。我尝试了左连接,但它似乎不起作用。交叉连接会给出错误的数据。

我拥有的表是 Person 表 (personID, Name)、TimeLog 表 (TimeLogID, StartDate, EndDate, TimeLogTypeID) 和 TimeLogType 表 (TimeLogTypeID, PersonID, Description, DeletedInd)

我能在结果集中得到的只是包含数据的行,而不是每个 TimeLogType 的空行

这是我目前所拥有的:

DECLARE 
    @startDate  DATE,
    @endDate    DATE

SET @startDate = '2014-05-01'
SET @endDate =   '2014-05-30'

SELECT
    CONVERT(DATE, TimeLog.StartDateTime, 101) AS TimeLogDay,
    SUM(dbo.fnCalculateHoursAsDecimal(TimeLog.StartDateTime, TimeLog.EndDateTime)) AS Hours,
    TimeLog.PersonID,
    TimeLog.TimeLogTypeID

INTO #HourTable

FROM
    TimeLog

WHERE   
    TimeLog.StartDateTime BETWEEN @startDate AND @endDate

GROUP BY
    CONVERT(DATE, TimeLog.StartDateTime, 101),
    TimeLog.TimeLogTypeID,  
    TimeLog.PersonID

SELECT 
    TimeLogType.Description,    
    #HourTable.*
FROM 
    TimeLogType LEFT JOIN
    #HourTable ON TimeLogType.TimeLogTypeID = #HourTable.TimeLogTypeID

WHERE 
    ISNULL(TimeLogType.DeletedInd, 0) = 0

ORDER BY
    PersonID, TimeLogDay, TimeLogType.TimeLogTypeID

数据是这样的:

时间日志类型:
1、可计费
2、不计费

人:
1、比利
2、汤姆

时间日志:
1, 1, 2014-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, 比利, Billiable, 2.0
2014-05-01,Billy,不可计费,NULL
2014-05-02, 比利, Billiable, 1.0
2014-05-02,Billy,不可计费,NULL
等等...
2014-05-01,汤姆,Billiable,0.5
2014-05-01,汤姆,不可开票,0.5
等等……

【问题讨论】:

  • 你能提供表格中的样本数据吗?
  • 和一个简短但描述性的预期输出示例

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


【解决方案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

【讨论】:

  • 我在使用 CTE 时遇到了一些语法错误。我不得不把“master..”放在 spt_values 表的前面,在连接之前将 subselect 上的别名从“v”更改为“dte”,但是“select dateadd(day, number - 1, min(date )) as thedate”在“min(date)”部分给我一个错误
  • @user3033282 。 . .对于那个很抱歉。立即尝试。
【解决方案2】:

在阅读了 Gordon 的回答后,这就是我想出的。我分步创建它,这样我就可以看到发生了什么。我创建了没有 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

【讨论】:

    猜你喜欢
    • 2011-10-16
    • 1970-01-01
    • 2013-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多