我在SQL Server数据库中有2个表。
客户主要费用表
ReportID CustomerID TotalExpenseAmount
1000 1 200
1001 2 600
参加者表
ReportID AttendeeName
1000 Mark
1000 Sam
1000 Joe
参与者级别没有金额。我需要手动计算单个与会者金额,如下所述。 (即根据与会者人数拆分TotalExpenseAmount,并确保将个别拆分数字舍入为2位小数,并总计达到TotalExpenseAmount)
最终报告应如下所示:
ReportID CustID AttendeeName TotalAmount AttendeeAmount
1000 1 Mark 200 66.66
1000 1 Sam 200 66.66
1000 1 Joe 200 66.68
最终报告将有大约1,50,000条记录。如果您注意到与会者数量,我已经将总数与总数匹配的方式舍入最后一个。在这种情况下,编写有效SQL查询的最佳方法是什么?
答案 0 :(得分:3)
您可以使用窗口功能执行此操作:
select ReportID, CustID, AttendeeName, TotalAmount,
(case when seqnum = 1
then TotalAmount - perAttendee * (cnt - 1)
else perAttendee
end) as AttendeeAmount
from (select a.ReportID, a.CustID, a.AttendeeName, e.TotalAmount,
row_number() over (partition by reportId order by AttendeeName) as seqnum,
count(*) over (partition by reportId) as cnt,
cast(TotalAmount * 1.0 / count(*) over (partition by reportId) as decimal(10, 2)) as perAttendee
from attendee a join
expense e
on a.ReportID = e.ReportID
) ae;
perAttendee
金额在子查询中计算。这是使用cast()
向下舍入的(仅因为floor()
不接受小数位参数)。对于其中一行,金额是总减去所有其他与会者的总和。
答案 1 :(得分:0)
做一些类似于@ Gordon的答案,但改用CTE。
with CTECount AS (
select a.ReportId, a.AttendeeName,
ROW_NUMBER() OVER (PARTITION BY A.ReportId ORDER BY A.AttendeeName) [RowNum],
COUNT(A.AttendeeName) OVER (PARTITION BY A.ReportId) [AttendeeCount],
CAST(c.TotalExpenseAmount / (COUNT(A.AttendeeName) OVER (PARTITION BY A.ReportId)) AS DECIMAL(10,2)) [PerAmount]
FROM @Customer C INNER JOIN @Attendee A ON A.ReportId = C.ReportID
)
SELECT CT.ReportID, CT.CustomerId, AT.AttendeeName,
CASE WHEN CC.RowNum = 1 THEN CT.TotalExpenseAmount - CC.PerAmount * (CC.AttendeeCount - 1)
ELSE CC.PerAmount END [AttendeeAmount]
FROM @Customer CT INNER JOIN @Attendee AT
ON CT.ReportID = AT.ReportId
INNER JOIN CTECount CC
ON CC.ReportId = CT.ReportID AND CC.AttendeeName = AT.AttendeeName
我喜欢CTE,因为它允许我分离查询的不同方面。 @Gordon使用的很酷的东西是Case语句和内部计算,以使行总计正确。