使用设置 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 EmployeeBonus,我每年都会在 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 JOINSUM() 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   |
+--------+-------+-------------+-----------+--------------------+