我有两个表:每个员工的总销售额列表和补偿层列表
Employee Sales | Comp Tiers
============== | ===================
EmpID Sales | TierID MaxAmt Rate
1 12000 | 1 10000 20% -- Up to $10k sales compensated at 20%
2 17000 | 2 15000 25% --The next $5k sales compensated at 25%
3 23000 | 3 20000 30% --The next $5k sales compensated at 30%
4 31000 | 4 25000 40% --The next $5k sales compensated at 40%
| 5 99999 50% --Any remaining sales compensated at 50%
根据这些输入,我需要根据每个层的MaxAmt值将每个员工的销售额分摊到每个层,以计算每个层的补偿率。此外,我不想对每个层进行硬编码,因为层数可能会随着时间的推移而变化。 (第二个想法,我不介意硬编码,只要它可以处理UP-TO 5层。听起来很公平吗?)
期望的输出:
EmpID Sales TierID TierAmt Rate Net
=========================================
1 12000 1 10000 20% 2000
1 12000 2 2000 25% 500
2 17000 1 10000 20% 2000
2 17000 2 5000 25% 1250
2 17000 3 2000 30% 600
3 23000 1 10000 20% 2000
3 23000 2 5000 25% 1250
3 23000 3 5000 30% 1500
3 23000 4 3000 40% 1200
4 31000 1 10000 20% 2000
4 31000 2 5000 25% 1250
4 31000 3 5000 30% 1500
4 31000 4 5000 40% 2000
4 31000 5 6000 50% 3000
我不熟悉SQL,但我甚至无法理解合适的策略。有任何想法吗?如果有助于实现目标,则允许对表结构进行更改。
答案 0 :(得分:3)
让我们制作一些测试数据:
DECLARE @EmpSales TABLE
(
EmpID INT,
Sales INT
)
INSERT INTO @EmpSales
VALUES
( 1, 12000 ),
( 2, 17000 ),
( 3, 23000 ),
( 4, 31000 );
DECLARE @CompTiers TABLE
(
TierID INT,
MaxAmount INT,
Rate DECIMAL(10,2)
)
INSERT INTO @CompTiers
VALUES
( 1, 10000, .20 ),
( 2, 15000, .25 ),
( 3, 20000, .30 ),
( 4, 25000, .40 ),
( 5, 99999, .50 );
在这里,我创建一个CTE来查找所有层和前一层(以获得层的顶部和底部)
WITH Tiers AS
(
SELECT
n.TierID,
n.MaxAmount,
n.Rate,
ISNULL(p.MaxAmount, 0) PrevAmount
FROM @CompTiers n
LEFT JOIN @CompTiers p
ON p.TierID = n.TierID - 1
),
让我们采取等级CTE并将其与销售选择交叉加入,只选择销售额大于预计数(层级底部)的层。
SalesComp AS
(
SELECT *
FROM @EmpSales e
CROSS JOIN Tiers c
WHERE Sales > PrevAmount
)
现在我们已经将数据匹配起来,只需要清理一些情况:
SELECT
s.EmpID,
s.Sales,
s.TierID,
CASE
WHEN s.Sales > s.MaxAmount THEN s.MaxAmount - s.PrevAmount
ELSE s.Sales - s.PrevAmount
END TierAmount,
s.Rate,
CASE
WHEN s.Sales > s.MaxAmount THEN (s.MaxAmount - s.PrevAmount) * s.Rate
ELSE (s.Sales - s.PrevAmount) * s.Rate
END Net
FROM SalesComp s
ORDER BY EmpID, TierID
这是输出:
EmpID Sales TierID TierAmount Rate Net
1 12000 1 10000 0.20 2000.00
1 12000 2 2000 0.25 500.00
2 17000 1 10000 0.20 2000.00
2 17000 2 5000 0.25 1250.00
2 17000 3 2000 0.30 600.00
3 23000 1 10000 0.20 2000.00
3 23000 2 5000 0.25 1250.00
3 23000 3 5000 0.30 1500.00
3 23000 4 3000 0.40 1200.00
4 31000 1 10000 0.20 2000.00
4 31000 2 5000 0.25 1250.00
4 31000 3 5000 0.30 1500.00
4 31000 4 5000 0.40 2000.00
4 31000 5 6000 0.50 3000.00
答案 1 :(得分:1)
the following query背后的想法恰好与Kevin Cook's answer中的想法相同,两种解决方案的主要区别在于语法:
WITH TierRanges AS (
SELECT
*,
MinAmt = LAG(MaxAmt, 1, 0) OVER (ORDER BY MaxAmt)
FROM
CompTiers
)
SELECT
s.EmpID,
s.Sales,
t.TierID,
x.TierAmt,
t.Rate,
Net = x.TierAmt * t.Rate
FROM
EmpSales AS s
INNER JOIN
TierRanges AS t ON s.Sales > t.MinAmt
CROSS APPLY
(
SELECT CASE WHEN s.Sales > t.MaxAmt THEN t.MaxAmt ELSE s.Sales END - t.MinAmt
) AS x (TierAmt)
ORDER BY
s.EmpID,
t.TierID
;
TierRanges
CTE“使用CompTiers
列增强”MinAmt
行集,与MaxAmt
一起形成一个等级范围,MinAmt
为先前tier的MaxAmt
值(或第一层的0)。这可以被认为是另一个答案的Tiers
CTE的直接等价物,但是你可以看到这里使用的是LAG函数而不是自联接,而前者的工作速度可能比后者。
主要查询将EmpSales
中的每一行与TierRanges
中EmpSales.Sales
超过TierRanges.MinAmt
的每一行连接起来。 (在另一个答案中,此部分与主查询分开实现为另一个CTE,SalesComp
。)要获取层数,它会从MinAmt
或{{}中减去Sales
值1}},取决于哪一个较小。因为在查询中需要两次层级数量(一次用于输出,第二次用于获取MaxAmt
),CROSS APPLY用于避免重复代码。
由于LAG,SQL Server 2012是运行查询所需的最低版本。
答案 2 :(得分:0)
尝试在表格之间使用完整的外部(交叉)联接,只要销售额小于或等于最高级别数量
答案 3 :(得分:0)
以Beth的建议作为起点,我通过其他方式努力提出一个可行的解决方案。它以CROSS JOIN开头,显示Sales和Tiers的所有可能组合,过滤掉Sales超出其匹配层的范围,并使用递归CTE使用先前的Tier结果计算每个Tier的计算。
;WITH
/* CROSS JOIN Sales and Tiers, and then ***/
/** filter to only relevant Tiers for each Emp. **/
/*** Also include Prior Tier [MaxAmt] value. */
combos AS
( SELECT s.EmpID,s.Sales,t.TierID,
[MaxAmt-1] = t0.MaxAmt,
[MaxAmt] = t.MaxAmt
FROM EmpSales s
CROSS JOIN CompTiers t
LEFT JOIN CompTiers t0 ON t0.TierID = t.TierID-1 --Prior Tier
WHERE s.Sales > ISNULL(t0.MaxAmt,0) --Filter out irrelevant Tiers given Sales
),
cte AS
/* Calculate Tier1 Sales, then use Recursive CTE **/
/** to calculate other Tiers based on prior Tier values */
( SELECT c.*,
TierAmt = CAST(CASE
WHEN c.Sales > c.[MaxAmt]
THEN c.[MaxAmt]
ELSE c.Sales END AS MONEY)
FROM combos c
WHERE c.TierID = 1
UNION ALL
SELECT c.*,
TierAmt = CAST(CASE
WHEN c.Sales > c.[MaxAmt]
THEN c.[MaxAmt]-ISNULL(c.[MaxAmt-1],0.0)
ELSE c.Sales-ISNULL(c.[MaxAmt-1],0) END AS MONEY)
FROM cte
JOIN combos c ON c.EmpID = cte.EmpID
WHERE c.TierID = cte.TierID+1
)
/* Combine [TierAmt] and [Rate] at each Tier */
/** to calculate Net Pay amount. **/
SELECT cte.EmpID,cte.Sales,cte.TierID,cte.TierAmt,
t.Rate,
Net = t.Rate * cte.TierAmt
FROM cte
LEFT JOIN CompTiers t ON t.TierID = cte.TierID
ORDER BY EmpID,TierID
我邀请反馈,改进和替代方案。