4

我们有一个时间管理系统,我们的员工或承包商(资源)输入他们的工作时间,我们为此计算成本。我有一张历史成本表:

CREATE TABLE ResourceTimeTypeCost (
 ResourceCode VARCHAR(32),
 TimeTypeCode VARCHAR(32),
 EffectiveDate DATETIME,
 CostRate DECIMAL(12,2)
)

所以我有一个日期字段来标记生效日期。如果我们有一个记录是

('ResourceA', 'Normal', '2012-04-30', 40.00)

我添加了一条记录

('ResourceA', 'Normal', '2012-05-04', 50.00) 

因此,4 月 30 日至 5 月 3 日之间输入的所有时间均为 40.00 英镑,4 日午夜之后的所有时间均为 50.00 英镑。我原则上理解这一点,但是您如何编写表达此逻辑的查询?

假设我的时间表如下所示

CREATE TABLE TimeEntered (
 ResourceCode VARCHAR(32),
 TimeTypeCode VARCHAR(32),
 ProjectCode VARCHAR(32),
 ActivityCode VARCHAR(32),
 TimeEnteredDate DATETIME,
 HoursWorked DECIMAL(12,2)
)

如果我将以下记录插入 TimeEntered 表

('ResourceA','Normal','Project1','Management1','2012-04-30',7.5)
('ResourceA','Normal','Project1','Management1','2012-05-01',7.5)
('ResourceA','Normal','Project1','Management1','2012-05-02',7.5)
('ResourceA','Normal','Project1','Management1','2012-05-03',7.5)
('ResourceA','Normal','Project1','Management1','2012-05-04',7.5)
('ResourceA','Normal','Project1','Management1','2012-05-07',7.5)
('ResourceA','Normal','Project1','Management1','2012-05-08',7.5)

我想获得一个按资源返回总成本的查询

所以在上面的例子中,它将是'ResourceA',(4 * 7.5 * 40) + (3 * 7.5 * 50) = 2325.00

谁能提供一个示例 SQL 查询?我知道这个例子没有使用 TimeType(即它总是“正常”),但我也想看看这是如何处理的

我无法更改数据库的结构。提前谢谢了

4

3 回答 3

3
IF OBJECT_ID ('tempdb..#ResourceTimeTypeCost') IS NOT NULL DROP TABLE #ResourceTimeTypeCost
CREATE TABLE #ResourceTimeTypeCost (  ResourceCode VARCHAR(32),  TimeTypeCode VARCHAR(32),  EffectiveDate DATETIME,  CostRate DECIMAL(12,2) ) 
INSERT INTO #ResourceTimeTypeCost 
SELECT 'ResourceA' as resourcecode, 'Normal' as timetypecode, '2012-04-30' as effectivedate, 40.00 as costrate
UNION ALL
SELECT 'ResourceA', 'Normal', '2012-05-04', 50.00

IF OBJECT_ID ('tempdb..#TimeEntered') IS NOT NULL DROP TABLE #TimeEntered
CREATE TABLE #TimeEntered (  ResourceCode VARCHAR(32),  TimeTypeCode VARCHAR(32),  ProjectCode VARCHAR(32),  ActivityCode VARCHAR(32),  TimeEnteredDate DATETIME,  HoursWorked DECIMAL(12,2) ) 
INSERT INTO #TimeEntered 
SELECT 'ResourceA','Normal','Project1','Management1','2012-04-30',7.5 
UNION ALL SELECT 'ResourceA','Normal','Project1','Management1','2012-05-01',7.5 
UNION ALL SELECT 'ResourceA','Normal','Project1','Management1','2012-05-02',7.5 
UNION ALL SELECT 'ResourceA','Normal','Project1','Management1','2012-05-03',7.5 
UNION ALL SELECT 'ResourceA','Normal','Project1','Management1','2012-05-04',7.5 
UNION ALL SELECT 'ResourceA','Normal','Project1','Management1','2012-05-07',7.5 
UNION ALL SELECT 'ResourceA','Normal','Project1','Management1','2012-05-08',7.5 

;with ranges as
(
select 
resourcecode 
,TimeTypeCode
,EffectiveDate
,costrate
,row_number() OVER (PARTITION BY resourcecode,timetypecode ORDER BY effectivedate ASC) as row
from #ResourceTimeTypeCost 
)
,ranges2 AS
(
SELECT 
r1.resourcecode 
,r1.TimeTypeCode
,r1.EffectiveDate
,r1.costrate
,r1.effectivedate as start_date
,ISNULL(DATEADD(ms,-3,r2.effectivedate),GETDATE()) as end_date
FROM ranges r1
LEFT OUTER JOIN ranges r2 on r2.row = r1.row + 1 --joins onto the next date row
                    AND r2.resourcecode = r1.resourcecode 
                    AND r2.TimeTypeCode = r1.TimeTypeCode
)
SELECT 
tee.resourcecode
,tee.timetypecode
,tee.projectcode
,tee.activitycode
,SUM(ranges2.costrate * tee.hoursworked) as total_cost
FROM #TimeEntered tee
INNER JOIN ranges2 ON tee.TimeEnteredDate >= ranges2.start_date
                    AND tee.TimeEnteredDate <= ranges2.end_date
                    AND tee.resourcecode = ranges2.resourcecode
                    AND tee.timetypecode = ranges2.TimeTypeCode
GROUP BY tee.resourcecode
,tee.timetypecode
,tee.projectcode
,tee.activitycode
于 2012-05-17T10:44:00.587 回答
3

你所拥有的是一个成本表,正如有些人所说,它是一个缓慢变化的维度。首先,这将有助于确定成本表的生效日期和结束日期。我们可以通过以下方式进行自我加入和分组:

with costs as
    (select c.ResourceCode, c.EffectiveDate as effdate,
            dateadd(day, -1, min(c1.EffectiveDate)) as endDate,
            datediff(day, c.EffectiveDate, c1.EffectiveDate) - 1 as Span
     from ResourceTimeTypeCost c left outer join
          ResourceTimeTypeCost c1
     group by c.ResourceCode, c.EffectiveDate
    )

虽然您说您不能更改表结构,但当您有一个缓慢变化的维度时,有一个有效和结束日期是一种很好的做法。

现在,您可以将此信息与 TimeEntered 一起使用,如下所示:

select te.*, c.CostRate * te.HoursWorked as dayCost
from TimeEntered te join
     Costs c
     on te.ResouceCode = c.ResourceCode and
        te.TimeEntered between c.EffDate and c.EndDate

要按给定时间范围内的资源进行汇总,完整的查询如下所示:

 with costs as
    (select c.ResourceCode, c.EffectiveDate as effdate,
            dateadd(day, -1, min(c1.EffectiveDate)) as endDate,
            datediff(day, c.EffectiveDate, c1.EffectiveDate) - 1 as Span
     from ResourceTimeTypeCost c left outer join
          ResourceTimeTypeCost c1
     group by c.ResourceCode, c.EffectiveDate
    ),
      te as
    (select te.*, c.CostRate * te.HoursWorked as dayCost
     from TimeEntered te join
          Costs c
          on te.ResouceCode = c.ResourceCode and
             te.TimeEntered between c.EffDate and c.EndDate
    )
select te.ResourceCode, sum(dayCost)
from te
where te.TimeEntered >= <date1> and te.TimeEntered < <date2>
于 2012-05-17T10:55:19.387 回答
2

你可以试试这个。CROSS APPLY将找到第一个 ResourceTimeTypeCost,其日期较早或相同,并且 ResourceCode 和 TimeTypeCode 与 TimeEntered 中的当前记录相同。

SELECT te.ResourceCode,
       te.TimeTypeCode,
       te.ProjectCode,
       te.ActivityCode,
       te.TimeEnteredDate,
       te.HoursWorked,
       te.HoursWorked * rttc.CostRate Cost
FROM TimeEntered te 
CROSS APPLY
(
  -- First one only
  SELECT top 1 CostRate
    FROM ResourceTimeTypeCost
   WHERE te.ResourceCode = ResourceTimeTypeCost.ResourceCode
     AND te.TimeTypeCode = ResourceTimeTypeCost.TimeTypeCode
     AND te.TimeEnteredDate >= ResourceTimeTypeCost.EffectiveDate
   -- By most recent date
   ORDER BY ResourceTimeTypeCost.EffectiveDate DESC
) rttc

不幸的是,我再也找不到关于 msdn 的文章,因此上面链接中的博客。

现场测试@Sql Fiddle

于 2012-05-17T10:45:13.527 回答