2012-05-17 26 views
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英鎊。我原則上理解這一點,但您如何撰寫表達此邏輯的查詢?

假設我的時間表看起來像下面

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(即它總是'正常'),但我想看看這是如何處理的

我不能改變數據庫的結構。非常感謝提前

+0

你需要構造一個視圖,CTE或子查詢來查找最近匹配的「resoucretimecost」記錄,而不是{join,multiply,sum}。 – wildplasser

+0

你使用什麼數據庫和版本? –

+0

SQL server 2008 – Jon

回答

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 
+0

感謝這個解決方案:它肯定給了我正確的答案,但我可以看到我有很多東西要學習這些「分區」。你有沒有從生效日期算起3毫秒的原因?我的本能(不知何故得到了這麼多!)會說改變連接條件爲 Jon

+0

@Jon我已經刪除了3毫秒,因爲這使得每個開始和結束範圍完全獨特(否則它們將重疊),並且3ms是DATETIME數據類型可存儲的最小單位。 – Dibstar

+0

哦,好的。如果跨越(resourcecode,timetypecode,effectivedate),我仍然可以使用此索引嗎? – Jon

2

您可以試試這個。 CROSS APPLY將會找到第一個ResourceTimeTypeCost與TimeEntered中的當前記錄具有較舊或相同的日期和相同的ResourceCode和TimeTypeCode。

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上找到文章,因此在上面的鏈接中的博客。

Live test @ Sql Fiddle

+0

這真是太棒了,當我只有SQL小提琴鏈接時,我會爲你+1!就我所擁有的三種解決方案而言,這是一種吸引我的大腦運作最多的方式。 CROSS APPLY是否適用於大多數SQL? – Jon

+0

@Jon謝謝。文檔說CROSS | OUTER APPLY自2005年以來一直與我們在一起。其他解決方案將ResourceTimeTypeCost轉換爲範圍,方法是將其連接到自身並移位1.然後查找TimeEnteredDate屬於哪個範圍是很簡單的。 –

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 
    ) 

:我們可以做一個自我得到這個聯接和GROUP BY。

現在,你可以使用這個信息來源與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 

要通過資源給定的時間範圍內總結,全面查詢看起來像:

​​3210
相關問題