英文:
Matching labour costs to a timesheet - rate per person per day
问题
Sure, here's the translated content:
"我在尝试找到每个员工在特定日期使用的正确费率时遇到了困难。
对于每个日期、员工ID和费率类型,找到正确的费率金额。然后,我需要高效地将这两者相乘。所示数据的格式与我在工作中要处理的数据类似。
工作时间表格式
日期 员工ID 费率 工作小时数 费率金额 劳动成本
3/01/2020 JBR001 BR1 8
10/01/2020 JBR001 BR1 8
10/01/2020 JBR001 OT1 4
17/01/2020 JBR001 BR1 8
24/01/2020 JBR001 BR1 8
31/01/2020 JBR001 BR1 8
7/02/2020 JBR001 BR1 8
14/02/2020 JBR001 BR1 8
13/03/2020 JBR001 BR1 8
20/03/2020 JBR001 BR1 8
20/03/2020 JBR001 OT1 4
27/03/2020 JBR001 BR1 8
3/04/2020 JBR001 BR1 8
17/04/2020 JBR001 BR1 8
4/01/2020 BJL001 BR1 8
10/01/2020 BJL001 BR1 8
27/02/2020 BJL001 BR1 8
27/02/2020 BJL001 OT1 3
4/03/2020 BJL001 BR1 8
10/03/2020 BJL001 BR1 8
10/03/2020 BJL001 OT1 4
28/03/2020 BJL001 BR1 8
3/04/2020 BJL001 BR1 8
劳动费率表
员工ID 费率 费率开始日期 费率金额
JBR001 BR1 1/01/2020 10
JBR001 BR1 1/03/2020 11
JBR001 OT1 1/01/2020 15
JBR001 OT1 1/04/2020 16
BJL001 BR1 1/01/2020 12
BJL001 BR1 1/03/2020 13
BJL001 OT1 1/01/2020 15
BJL001 OT1 1/04/2020 16
如果有人能告诉我如何将这些信息合并在一起,我将非常感激。
谢谢!
我已经成功地在PowerQuery/M中组合了这些信息,但查询非常慢。我希望有人能与我分享T-SQL的威力!"
英文:
I'm having difficulty trying to find the correct rate to use for each employee on a given date.
For each Date, Employee ID and Rate Type, Find the correct Rate Amount. I then need to multiply those two efficiently. The data shown is similar in format to what I'm trying to process in my role.
Timesheet Format
Date EmpID Rate HoursWorked RateAmount LabourCost
3/01/2020 JBR001 BR1 8
10/01/2020 JBR001 BR1 8
10/01/2020 JBR001 OT1 4
17/01/2020 JBR001 BR1 8
24/01/2020 JBR001 BR1 8
31/01/2020 JBR001 BR1 8
7/02/2020 JBR001 BR1 8
14/02/2020 JBR001 BR1 8
13/03/2020 JBR001 BR1 8
20/03/2020 JBR001 BR1 8
20/03/2020 JBR001 OT1 4
27/03/2020 JBR001 BR1 8
3/04/2020 JBR001 BR1 8
17/04/2020 JBR001 BR1 8
4/01/2020 BJL001 BR1 8
10/01/2020 BJL001 BR1 8
27/02/2020 BJL001 BR1 8
27/02/2020 BJL001 OT1 3
4/03/2020 BJL001 BR1 8
10/03/2020 BJL001 BR1 8
10/03/2020 BJL001 OT1 4
28/03/2020 BJL001 BR1 8
3/04/2020 BJL001 BR1 8
Labour Rate Table
EmpID Rate RateStart RateAmount
JBR001 BR1 1/01/2020 10
JBR001 BR1 1/03/2020 11
JBR001 OT1 1/01/2020 15
JBR001 OT1 1/04/2020 16
BJL001 BR1 1/01/2020 12
BJL001 BR1 1/03/2020 13
BJL001 OT1 1/01/2020 15
BJL001 OT1 1/04/2020 16
If somebody could advise me how to go about mashing this information together I would be very grateful.
Thank you!
I have been successful in combining this information in PowerQuery/M, but the query was very very slow. I'm hoping somebody could share the power of T-SQL with me!
答案1
得分: 1
以下是翻译好的代码部分:
获取 Timesheet 条目的有效 RateAmount 的一种方法是使用相关的 TOP 1 查询([示例][1])
SELECT TS.*,
LR.RateAmount
FROM Timesheet TS
CROSS APPLY (SELECT TOP 1 *
FROM LabourRate LR
WHERE TS.EmpId = LR.EmpId
AND TS.Rate = LR.Rate
AND LR.RateStart <= TS.Date
ORDER BY LR.RateStart DESC) LR
上述查询需要在 LabourRate 表上建立索引
LabourRate(EmpId,Rate,RateStart) INCLUDE ( RateAmount)
如果表不太大,上述方法可能足够 - 不过还有其他潜在更有效的方法。
如果你使用的是 SQL Server 2022,可以考虑使用 `LAST_VALUE` 的 `IGNORE NULLS` 选项来跟踪有效汇率,而不需要相关的查询([示例 2][2])
WITH T1 AS
(
SELECT 1 AS Code, [Date], EmpId, Rate, HoursWorked, NULL AS RateAmount
FROM Timesheet
UNION ALL
SELECT 0 AS Code, [RateStart],[EmpId], [Rate], NULL, RateAmount
FROM LabourRate
), T2 AS
(
SELECT *,
EffectiveRateAmount = LAST_VALUE(RateAmount) IGNORE NULLS OVER (PARTITION BY EmpId, Rate ORDER BY [Date], Code)
FROM T1
)
SELECT [Date], EmpId, Rate, HoursWorked, EffectiveRateAmount
FROM T2
WHERE Code = 1
英文:
One way of getting the effective RateAmount
for a Timesheet entry is a correlated TOP 1
query (Fiddle)
SELECT TS.*,
LR.RateAmount
FROM Timesheet TS
CROSS APPLY (SELECT TOP 1 *
FROM LabourRate LR
WHERE TS.EmpId = LR.EmpId
AND TS.Rate = LR.Rate
AND LR.RateStart <= TS.Date
ORDER BY LR.RateStart DESC) LR
The above will need an index on
LabourRate(EmpId,Rate,RateStart) INCLUDE ( RateAmount)
If the tables aren't too large the above will likely be adequate - though there are other potentially more efficient ways.
If you are on SQL Server 2022 the IGNORE NULLS
option to LAST_VALUE
could also potentially be useful here to keep track of the effective rate without needing a correlated query (Fiddle 2)
WITH T1 AS
(
SELECT 1 AS Code, [Date], EmpId, Rate, HoursWorked, NULL AS RateAmount
FROM Timesheet
UNION ALL
SELECT 0 AS Code, [RateStart],[EmpId], [Rate], NULL, RateAmount
FROM LabourRate
), T2 AS
(
SELECT *,
EffectiveRateAmount = LAST_VALUE(RateAmount) IGNORE NULLS OVER (PARTITION BY EmpId, Rate ORDER BY [Date], Code)
FROM T1
)
SELECT [Date], EmpId, Rate, HoursWorked, EffectiveRateAmount
FROM T2
WHERE Code = 1
答案2
得分: 1
你可以使用CROSS APPLY(SELECT TOP 1 ...)
模式来为每个工作记录选择适当的费率金额。CROSS APPLY
类似于与子查询的连接,该子查询可以引用FROM
子句中较早的表。
在你的情况下,看起来你需要匹配EmpID
和Rate
,然后选择劳动费率表中RateStart
值等于或小于工作时间的最新行。
可以按如下方式完成:
SELECT T.*, R.RateAmount, T.HoursWorked * R.RateAmount AS LabourCost
FROM TimeSheet T
OUTER APPLY (
-- 截至工作时间的最近费率
SELECT TOP 1 *
FROM LabourRateTable R
WHERE R.EmpID = T.EmpID
AND R.Rate = T.Rate
AND R.RateStart <= T.Date
ORDER BY R.RateStart DESC
) R
ORDER BY T.EmpID, T.Date
请注意,我使用了OUTER APPLY
而不是CROSS APPLY
。OUTER APPLY
类似于CROSS APPLY
的左连接变体。如果找不到匹配项,它将返回一个结果,其RateAmount值为null(并且计算的LabourCost也为null)。如果你的数据良好,这不应发生,但与悄悄地丢弃不匹配的数据相比,可能更倾向于返回null值。
在这个 db<>fiddle 中有一个演示。
示例结果:
日期 | EmpID | 费率 | 工作小时数 | 费率金额 | 劳动成本 |
---|---|---|---|---|---|
2020-01-04 | BJL001 | BR1 | 8 | 12 | 96 |
2020-01-10 | BJL001 | BR1 | 8 | 12 | 96 |
2020-02-27 | BJL001 | BR1 | 8 | 12 | 96 |
2020-02-27 | BJL001 | OT1 | 3 | 15 | 45 |
2020-03-04 | BJL001 | BR1 | 8 | 13 | 104 |
2020-03-10 | BJL001 | BR1 | 8 | 13 | 104 |
2020-03-10 | BJL001 | OT1 | 4 | 15 | 60 |
2020-03-28 | BJL001 | BR1 | 8 | 13 | 104 |
2020-04-03 | BJL001 | BR1 | 8 | 13 | 104 |
2020-01-03 | JBR001 | BR1 | 8 | 10 | 80 |
2020-01-10 | JBR001 | BR1 | 8 | 10 | 80 |
2020-01-10 | JBR001 | OT1 | 4 | 15 | 60 |
2020-01-17 | JBR001 | BR1 | 8 | 10 | 80 |
2020-01-24 | JBR001 | BR1 | 8 | 10 | 80 |
2020-01-31 | JBR001 | BR1 | 8 | 10 | 80 |
2020-02-07 | JBR001 | BR1 | 8 | 10 | 80 |
2020-02-14 | JBR001 | BR1 | 8 | 10 | 80 |
2020-03-13 | JBR001 | BR1 | 8 | 11 | 88 |
2020-03-20 | JBR001 | BR1 | 8 | 11 | 88 |
2020-03-20 | JBR001 | OT1 | 4 | 15 | 60 |
2020-03-27 | JBR001 | BR1 | 8 | 11 | 88 |
2020-04-03 | JBR001 | BR1 | 8 | 11 | 88 |
2020-04-17 | JBR001 | BR1 | 8 | 11 | 88 |
英文:
You can use a CROSS APPLY(SELECT TOP 1 ...)
pattern to select the appropriate rate amount for each timesheet record. A CROSS APPLY
is like a join to a subquery, where that subquery can reference tables earlier in the FROM
clause.
In your case, it appears that you need to match on EmpID
and Rate
, and then select the latest Labour Rate Table row with a RateStart value equal or less than the timesheet date.
This can be done as follows:
SELECT T.*, R.RateAmount, T.HoursWorked * R.RateAmount AS LabourCost
FROM TimeSheet T
OUTER APPLY (
-- Most recent rate as of timesheet date
SELECT TOP 1 *
FROM LabourRateTable R
WHERE R.EmpID = T.EmpID
AND R.Rate = T.Rate
AND R.RateStart <= T.Date
ORDER BY R.RateStart DESC
) R
ORDER BY T.EmpID, T.Date
Note that I used an OUTER APPLY
instead of a CROSS APPLY
. An outer apply is like a left join variant of a cross apply. If no match is found, it will still return a result having null RateAmount value (and null calculated LabourCost). This should not occur if your data is good, but returning null values may be preferred over quietly discarding unmatched data.
See this db<>fiddle for a demo.
Sample results:
Date | EmpID | Rate | HoursWorked | RateAmount | LabourCost |
---|---|---|---|---|---|
2020-01-04 | BJL001 | BR1 | 8 | 12 | 96 |
2020-01-10 | BJL001 | BR1 | 8 | 12 | 96 |
2020-02-27 | BJL001 | BR1 | 8 | 12 | 96 |
2020-02-27 | BJL001 | OT1 | 3 | 15 | 45 |
2020-03-04 | BJL001 | BR1 | 8 | 13 | 104 |
2020-03-10 | BJL001 | BR1 | 8 | 13 | 104 |
2020-03-10 | BJL001 | OT1 | 4 | 15 | 60 |
2020-03-28 | BJL001 | BR1 | 8 | 13 | 104 |
2020-04-03 | BJL001 | BR1 | 8 | 13 | 104 |
2020-01-03 | JBR001 | BR1 | 8 | 10 | 80 |
2020-01-10 | JBR001 | BR1 | 8 | 10 | 80 |
2020-01-10 | JBR001 | OT1 | 4 | 15 | 60 |
2020-01-17 | JBR001 | BR1 | 8 | 10 | 80 |
2020-01-24 | JBR001 | BR1 | 8 | 10 | 80 |
2020-01-31 | JBR001 | BR1 | 8 | 10 | 80 |
2020-02-07 | JBR001 | BR1 | 8 | 10 | 80 |
2020-02-14 | JBR001 | BR1 | 8 | 10 | 80 |
2020-03-13 | JBR001 | BR1 | 8 | 11 | 88 |
2020-03-20 | JBR001 | BR1 | 8 | 11 | 88 |
2020-03-20 | JBR001 | OT1 | 4 | 15 | 60 |
2020-03-27 | JBR001 | BR1 | 8 | 11 | 88 |
2020-04-03 | JBR001 | BR1 | 8 | 11 | 88 |
2020-04-17 | JBR001 | BR1 | 8 | 11 | 88 |
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论