使用设置 table 按比例分配奖金
Pro rata basis bonus distribution using setup table
CREATE TABLE #empInfo(emp_id INT, dept_id INT, salary INT)
CREATE TABLE #empBonus(dep_id INT, emp_id INT, bonus INT)
我上面有两个 tables Employee
和 Bonus
,我每年都会在 bonus table 中为员工分配奖金,但在示例中我们是打算只做一年,所以没有给出年份列。
INSERT INTO #empInfo VALUES
(111, 100, 5000),
(112, 100, 4000),
(113, 100, 4000),
(114, 100, 3500),
(115, 100, 4500),
(116, 100, 3000),
(114, 200, 3500),
(115, 200, 4500),
(116, 200, 3000),
(114, 300, 3500),
(115, 300, 3500),
(116, 300, 3500)
INSERT INTO #empBonus VALUES
(100, 111, 1000),
(100, NULL, 4000),
(100, 111, 500),
(100, NULL, 4000),
(100, 113, 700),
(200, 114, 600),
(200, NULL, 1600),
(300, 116, 900)
以上,如果员工 ID 在 empBonus
table 中定义,则奖金应分配给该员工,如果为空,则意味着奖金分配给未在 empBonus
中列出的所有员工并根据薪水获得奖金。
我们可以为多个员工定义bonus,同一员工可以有多个bonus,这种情况下我们要对bonus总和进行相应的运算。 NULL 也是同样的情况。
例如,基于下面给出的公式,我在 EXCEL
中做了以下计算以便于理解,在 SQL
中我正在尝试使用 OUTER APPLY
但没有得到我想要的想从单个查询中获取?
--Formula = bonus*salary/totSalary(of respective group or employee)
DeptID EmpID TotBonus Salary TotSalary Bonus
100 111 1500 5000 5000 1500.00000000000
100 112 8000 4000 15000 2133.33333333333
100 113 700 4000 4000 700.00000000000
100 114 8000 3500 15000 1866.66666666666
100 115 8000 4500 15000 2400.00000000000
100 116 8000 3000 15000 1600.00000000000
200 114 600 3500 3500 600.00000000000
200 115 1600 4500 7500 960.00000000000
200 116 1600 3000 7500 640.00000000000
300 114 0 3500 7000 0.00000000000
300 115 0 3500 7000 0.00000000000
300 116 900 3500 3500 900.00000000000
任何帮助将不胜感激,提前致谢:)
嗯,这对我来说是一个很好的挑战。
首先创建cte是计算TotSalary列:
;With cteTotalSalary as
(
-- select total salary for employees that are in the bonus table
SELECT e.emp_id, dept_id, Salary, Salary As TotSalary
FROM #empInfo e
INNER JOIN #empBonus b ON e.dept_id = b.dep_id AND e.emp_id = b.emp_id
UNION
-- select total salary for employees that are in NOT the bonus table
SELECT e.emp_id, dept_id, Salary, SUM(Salary) OVER(PARTITION BY dept_id) As TotSalary
FROM #empInfo e
WHERE EXISTS (
SELECT 1
FROM #empBonus b
WHERE e.dept_id = b.dep_id
AND b.emp_id IS NULL
)
AND NOT EXISTS
(
SELECT 1
FROM #empBonus b
WHERE e.dept_id = b.dep_id
AND e.emp_id = b.emp_id
)
)
然后,用union查询这个cte两次,得到两种bonus(员工bonus和部门bonus)
-- Get the bonus of the employess that exists in the empBonus table
SELECT c.emp_id, dept_id, SUM(Bonus) OVER(PARTITION BY c.emp_id) as Bonus, Salary, TotSalary, CAST(SUM(CAST(Bonus as decimal)) OVER(PARTITION BY c.emp_id) as decimal) as [Bonus Distribution]
FROM cteTotalSalary c
INNER JOIN #empBonus b ON c.dept_id = b.dep_id AND c.emp_id = b.emp_id
UNION
-- Get the bonus of the employees that does not exists in the empBonus table
SELECT c.emp_id, dept_id, SUM(Bonus) OVER(PARTITION BY c.emp_id), Salary, TotSalary, SUM(CAST(Bonus as decimal) * Salary / TotSalary) OVER(PARTITION BY c.emp_id)
FROM cteTotalSalary c
INNER JOIN #empBonus b ON c.dept_id = b.dep_id AND b.emp_id IS NULL
AND NOT EXISTS (
SELECT 1
FROM #empBonus b
WHERE c.dept_id = b.dep_id
AND c.emp_id = b.emp_id
)
结果:
emp_id dept_id Bonus Salary TotSalary Bonus Distribution
111 100 1500 5000 5000 1500.000000000
112 100 8000 4000 19000 1684.210526314
113 100 8000 4000 19000 1684.210526314
114 100 8000 3500 19000 1473.684210526
115 100 8000 4500 19000 1894.736842104
116 100 8000 3000 19000 1263.157894736
你可以看到它的实际效果here
这是使用 FULL OUTER JOIN
和 SUM() OVER()
Window 聚合
的一种方法
;WITH cte
AS (SELECT ei.emp_id,ei.dept_id, eb.dep_id,
bonus = COALESCE(bonus, Max(CASE WHEN eb.emp_id IS NULL THEN bonus END)
OVER( partition BY COALESCE(ei.dept_id, eb.dep_id) )),
salary = Cast(salary AS NUMERIC(22, 6)),
TotSalary= Iif(eb.emp_id IS NULL, Sum(CASE WHEN eb.emp_id IS NULL THEN salary END)
OVER(partition by ei.dept_id), salary)
FROM #empInfo ei
FULL OUTER JOIN (SELECT bonus= Sum(bonus),
dep_id,
emp_id
FROM #empBonus
GROUP BY dep_id,
emp_id) eb
ON ei.dept_id = eb.dep_id
AND eb.emp_id = ei.emp_id)
SELECT emp_id,
bonus,
salary,
TotSalary,
( bonus * salary ) / NULLIF(TotSalary, 0)
FROM cte
WHERE emp_id IS NOT NULL
结果:
+--------+-------+-------------+-----------+--------------------+
| emp_id | bonus | salary | TotSalary | Bonus Distribution |
+--------+-------+-------------+-----------+--------------------+
| 111 | 1500 | 5000.000000 | 5000 | 1500.00000000000 |
| 112 | 8000 | 4000.000000 | 19000 | 1684.21052631578 |
| 113 | 8000 | 4000.000000 | 19000 | 1684.21052631578 |
| 114 | 8000 | 3500.000000 | 19000 | 1473.68421052631 |
| 115 | 8000 | 4500.000000 | 19000 | 1894.73684210526 |
| 116 | 8000 | 3000.000000 | 19000 | 1263.15789473684 |
+--------+-------+-------------+-----------+--------------------+
CREATE TABLE #empInfo(emp_id INT, dept_id INT, salary INT)
CREATE TABLE #empBonus(dep_id INT, emp_id INT, bonus INT)
我上面有两个 tables Employee
和 Bonus
,我每年都会在 bonus table 中为员工分配奖金,但在示例中我们是打算只做一年,所以没有给出年份列。
INSERT INTO #empInfo VALUES
(111, 100, 5000),
(112, 100, 4000),
(113, 100, 4000),
(114, 100, 3500),
(115, 100, 4500),
(116, 100, 3000),
(114, 200, 3500),
(115, 200, 4500),
(116, 200, 3000),
(114, 300, 3500),
(115, 300, 3500),
(116, 300, 3500)
INSERT INTO #empBonus VALUES
(100, 111, 1000),
(100, NULL, 4000),
(100, 111, 500),
(100, NULL, 4000),
(100, 113, 700),
(200, 114, 600),
(200, NULL, 1600),
(300, 116, 900)
以上,如果员工 ID 在 empBonus
table 中定义,则奖金应分配给该员工,如果为空,则意味着奖金分配给未在 empBonus
中列出的所有员工并根据薪水获得奖金。
我们可以为多个员工定义bonus,同一员工可以有多个bonus,这种情况下我们要对bonus总和进行相应的运算。 NULL 也是同样的情况。
例如,基于下面给出的公式,我在 EXCEL
中做了以下计算以便于理解,在 SQL
中我正在尝试使用 OUTER APPLY
但没有得到我想要的想从单个查询中获取?
--Formula = bonus*salary/totSalary(of respective group or employee)
DeptID EmpID TotBonus Salary TotSalary Bonus
100 111 1500 5000 5000 1500.00000000000
100 112 8000 4000 15000 2133.33333333333
100 113 700 4000 4000 700.00000000000
100 114 8000 3500 15000 1866.66666666666
100 115 8000 4500 15000 2400.00000000000
100 116 8000 3000 15000 1600.00000000000
200 114 600 3500 3500 600.00000000000
200 115 1600 4500 7500 960.00000000000
200 116 1600 3000 7500 640.00000000000
300 114 0 3500 7000 0.00000000000
300 115 0 3500 7000 0.00000000000
300 116 900 3500 3500 900.00000000000
任何帮助将不胜感激,提前致谢:)
嗯,这对我来说是一个很好的挑战。
首先创建cte是计算TotSalary列:
;With cteTotalSalary as
(
-- select total salary for employees that are in the bonus table
SELECT e.emp_id, dept_id, Salary, Salary As TotSalary
FROM #empInfo e
INNER JOIN #empBonus b ON e.dept_id = b.dep_id AND e.emp_id = b.emp_id
UNION
-- select total salary for employees that are in NOT the bonus table
SELECT e.emp_id, dept_id, Salary, SUM(Salary) OVER(PARTITION BY dept_id) As TotSalary
FROM #empInfo e
WHERE EXISTS (
SELECT 1
FROM #empBonus b
WHERE e.dept_id = b.dep_id
AND b.emp_id IS NULL
)
AND NOT EXISTS
(
SELECT 1
FROM #empBonus b
WHERE e.dept_id = b.dep_id
AND e.emp_id = b.emp_id
)
)
然后,用union查询这个cte两次,得到两种bonus(员工bonus和部门bonus)
-- Get the bonus of the employess that exists in the empBonus table
SELECT c.emp_id, dept_id, SUM(Bonus) OVER(PARTITION BY c.emp_id) as Bonus, Salary, TotSalary, CAST(SUM(CAST(Bonus as decimal)) OVER(PARTITION BY c.emp_id) as decimal) as [Bonus Distribution]
FROM cteTotalSalary c
INNER JOIN #empBonus b ON c.dept_id = b.dep_id AND c.emp_id = b.emp_id
UNION
-- Get the bonus of the employees that does not exists in the empBonus table
SELECT c.emp_id, dept_id, SUM(Bonus) OVER(PARTITION BY c.emp_id), Salary, TotSalary, SUM(CAST(Bonus as decimal) * Salary / TotSalary) OVER(PARTITION BY c.emp_id)
FROM cteTotalSalary c
INNER JOIN #empBonus b ON c.dept_id = b.dep_id AND b.emp_id IS NULL
AND NOT EXISTS (
SELECT 1
FROM #empBonus b
WHERE c.dept_id = b.dep_id
AND c.emp_id = b.emp_id
)
结果:
emp_id dept_id Bonus Salary TotSalary Bonus Distribution
111 100 1500 5000 5000 1500.000000000
112 100 8000 4000 19000 1684.210526314
113 100 8000 4000 19000 1684.210526314
114 100 8000 3500 19000 1473.684210526
115 100 8000 4500 19000 1894.736842104
116 100 8000 3000 19000 1263.157894736
你可以看到它的实际效果here
这是使用 FULL OUTER JOIN
和 SUM() OVER()
Window 聚合
;WITH cte
AS (SELECT ei.emp_id,ei.dept_id, eb.dep_id,
bonus = COALESCE(bonus, Max(CASE WHEN eb.emp_id IS NULL THEN bonus END)
OVER( partition BY COALESCE(ei.dept_id, eb.dep_id) )),
salary = Cast(salary AS NUMERIC(22, 6)),
TotSalary= Iif(eb.emp_id IS NULL, Sum(CASE WHEN eb.emp_id IS NULL THEN salary END)
OVER(partition by ei.dept_id), salary)
FROM #empInfo ei
FULL OUTER JOIN (SELECT bonus= Sum(bonus),
dep_id,
emp_id
FROM #empBonus
GROUP BY dep_id,
emp_id) eb
ON ei.dept_id = eb.dep_id
AND eb.emp_id = ei.emp_id)
SELECT emp_id,
bonus,
salary,
TotSalary,
( bonus * salary ) / NULLIF(TotSalary, 0)
FROM cte
WHERE emp_id IS NOT NULL
结果:
+--------+-------+-------------+-----------+--------------------+
| emp_id | bonus | salary | TotSalary | Bonus Distribution |
+--------+-------+-------------+-----------+--------------------+
| 111 | 1500 | 5000.000000 | 5000 | 1500.00000000000 |
| 112 | 8000 | 4000.000000 | 19000 | 1684.21052631578 |
| 113 | 8000 | 4000.000000 | 19000 | 1684.21052631578 |
| 114 | 8000 | 3500.000000 | 19000 | 1473.68421052631 |
| 115 | 8000 | 4500.000000 | 19000 | 1894.73684210526 |
| 116 | 8000 | 3000.000000 | 19000 | 1263.15789473684 |
+--------+-------+-------------+-----------+--------------------+