sql获取自引用表中的平均工资

标签 sql sql-server aggregate hierarchy

我已经看到了几个关于如何递归查询自引用表的问题/答案,但我正在努力将我找到的答案应用到每个 parent 、祖 parent 等身上,无论该项目在哪里位于层次结构中。

需要获得每个部门(包括层次结构)的平均工资。 即部门应包括各子部门的平均工资等。

我有 nex 数据库架构:

CREATE TABLE Employee
    (
      Id INT NOT NULL ,
      Name VARCHAR(200) NOT NULL ,
      Department_Id INT NOT NULL ,
      PRIMARY KEY ( Id )
    ); 

CREATE TABLE Department
    (
      Id INT NOT NULL ,
      DepartmentName VARCHAR(200) NOT NULL ,
      Parent_Id INT ,
      PRIMARY KEY ( Id )
    );

CREATE TABLE Salary
    (
      Id INT NOT NULL ,
      Date DATETIME NOT NULL ,
      Amount INT NOT NULL ,
      Employee_Id INT NOT NULL ,
      PRIMARY KEY ( Id )
    );

我已经尝试过类似的方法,但它仅包含层次结构的第一级。

SELECT  d.Id ,
        d.DepartmentName ,
        ( SELECT    AVG(s.Amount)
                FROM      dbo.Department dd
                        LEFT JOIN dbo.Department sdd ON dd.Id = sdd.Parent_Id 
                        JOIN dbo.Employee e ON e.Department_Id = sdd.Id
                                                OR e.Department_Id = dd.Id
                        JOIN dbo.Salary s ON s.Employee_Id = e.Id
                                                WHERE dd.Id = d.Id
        ) AS avg_dep_salary 
FROM    dbo.Department d
WHERE   d.Parent_Id IS NULL;

怎样才能得到各个级别的平均工资?

编辑:添加了一些插入内容

INSERT  INTO Employee
        ( Id, Name, Department_Id )
VALUES  ( 1, 'Peter', 1 ),
        ( 2, 'Alex', 1 ),
        ( 3, 'Sam', 2 ),
        ( 4, 'James', 2 ),
        ( 5, 'Anna', 3 ),
        ( 6, 'Susan', 3 ),
        ( 7, 'Abby', 4 ),
        ( 8, 'Endy', 4 );

INSERT  INTO Department
        ( Id, DepartmentName, Parent_Id )
VALUES  ( 1, 'IT', NULL ),
        ( 2, 'HR', NULL),
        ( 3, 'SubIT', 1 ),
        ( 4, 'SubSubIT', 3 );

INSERT  INTO Salary
        ( Id, Date, Amount, Employee_Id )
VALUES  ( 1, '2013-01-09 16:03:50.003', 3000, 1 ),
        ( 2, '2013-01-11 16:03:50.003', 5000, 2 ),
        ( 3, '2013-01-09 16:03:50.003', 2000, 3 ),
        ( 4, '2013-01-11 16:03:50.003', 1000, 4 ),
        ( 5, '2013-01-09 16:03:50.003', 4000, 5 ),
        ( 6, '2013-01-11 16:03:50.003', 6000, 6 ),
        ( 7, '2013-01-09 16:03:50.003', 7000, 7 ),
        ( 8, '2013-01-13 16:03:50.003', 9000, 8 );

预期结果是:

Department | Average_Salary
__________________________________
IT         |  ( X1 + X2 + X3 ) / 3
HR         |  ( Y1 ) / 1
SubIT      |  ( X2 + X3 ) / 2
SubSubIT   |  ( X3 ) / 1

地点:

  • X1 - IT 部门的平均工资
  • X2 - SubIT 部门的平均工资
  • X3 - SubSubIT 部门的平均工资
  • Y1 - 平均 人力资源部薪资

最佳答案

示例数据

我添加了几行具有更宽的树结构。

DECLARE @Employee TABLE
(
Id INT NOT NULL ,
Name VARCHAR(200) NOT NULL ,
Department_Id INT NOT NULL ,
PRIMARY KEY ( Id )
); 

DECLARE @Department TABLE
(
Id INT NOT NULL ,
DepartmentName VARCHAR(200) NOT NULL ,
Parent_Id INT ,
PRIMARY KEY ( Id )
);

DECLARE @Salary TABLE
(
Id INT NOT NULL ,
Date DATETIME NOT NULL ,
Amount INT NOT NULL ,
Employee_Id INT NOT NULL ,
PRIMARY KEY ( Id )
);

INSERT  INTO @Employee
( Id, Name, Department_Id )
VALUES  
( 1, 'Peter', 1 ),
( 2, 'Alex',  1 ),
( 3, 'Sam',   2 ),
( 4, 'James', 2 ),
( 5, 'Anna',  3 ),
( 6, 'Susan', 3 ),
( 7, 'Abby',  4 ),
( 8, 'Endy',  4 ),
(10, 'e_A',   10),
(11, 'e_AB',  11),
(12, 'e_AC',  12),
(13, 'e_AD',  13),
(14, 'e_ACE', 14),
(15, 'e_ACF', 15),
(16, 'e_ACG', 16);

INSERT  INTO @Department
( Id, DepartmentName, Parent_Id )
VALUES  
( 1, 'IT', NULL ),
( 2, 'HR', NULL),
( 3, 'SubIT', 1 ),
( 4, 'SubSubIT', 3 ),
(10, 'A', NULL ),
(11, 'AB', 10),
(12, 'AC', 10),
(13, 'AD', 10),
(14, 'ACE', 12),
(15, 'ACF', 12),
(16, 'ACG', 12);

INSERT  INTO @Salary
( Id, Date, Amount, Employee_Id )
VALUES  
( 1, '2013-01-09 16:03:50.003', 3000, 1 ),
( 2, '2013-01-11 16:03:50.003', 5000, 2 ),
( 3, '2013-01-09 16:03:50.003', 2000, 3 ),
( 4, '2013-01-11 16:03:50.003', 1000, 4 ),
( 5, '2013-01-09 16:03:50.003', 4000, 5 ),
( 6, '2013-01-11 16:03:50.003', 6000, 6 ),
( 7, '2013-01-09 16:03:50.003', 7000, 7 ),
( 8, '2013-01-13 16:03:50.003', 9000, 8 ),
(10, '2013-01-13 16:03:50', 100, 10),
(11, '2013-01-13 16:03:50', 100, 11),
(12, '2013-01-13 16:03:50', 100, 12),
(13, '2013-01-13 16:03:50', 100, 13),
(14, '2013-01-13 16:03:50', 100, 14),
(15, '2013-01-13 16:03:50', 100, 15),
(16, '2013-01-13 16:03:50', 100, 16);

查询

WITH
CTE_Departments
AS
(
    SELECT
        D.Id
        ,D.Parent_Id
        ,D.DepartmentName
        ,SUM(Amount) AS DepartmentAmount
        ,COUNT(*) AS DepartmentCount
    FROM
        @Department AS D
        INNER JOIN @Employee AS E ON E.Department_Id = D.Id
        INNER JOIN @Salary AS S ON S.Employee_Id = E.Id
    GROUP BY
        D.Id
        ,D.Parent_Id
        ,D.DepartmentName
)
,CTE_Recursive
AS
(
    SELECT
         CTE_Departments.Id AS OriginalID
        ,CTE_Departments.DepartmentName AS OriginalName
        ,CTE_Departments.Id
        ,CTE_Departments.Parent_Id
        ,CTE_Departments.DepartmentName
        ,CTE_Departments.DepartmentAmount
        ,CTE_Departments.DepartmentCount
        ,1 AS Lvl
    FROM CTE_Departments

    UNION ALL

    SELECT
         CTE_Recursive.OriginalID
        ,CTE_Recursive.OriginalName
        ,CTE_Departments.Id
        ,CTE_Departments.Parent_Id
        ,CTE_Departments.DepartmentName
        ,CTE_Departments.DepartmentAmount
        ,CTE_Departments.DepartmentCount
        ,CTE_Recursive.Lvl + 1 AS Lvl
    FROM
        CTE_Departments
        INNER JOIN CTE_Recursive ON CTE_Recursive.Id = CTE_Departments.Parent_Id
)
SELECT
    OriginalID
    ,OriginalName
    ,SUM(DepartmentAmount) AS SumAmount
    ,SUM(DepartmentCount) AS SumCount
    ,SUM(DepartmentAmount) / SUM(DepartmentCount) AS AvgAmount
FROM CTE_Recursive
GROUP BY
    OriginalID
    ,OriginalName
ORDER BY OriginalID
;

结果

+------------+--------------+-----------+----------+-----------+
| OriginalID | OriginalName | SumAmount | SumCount | AvgAmount |
+------------+--------------+-----------+----------+-----------+
|          1 | IT           |     34000 |        6 |      5666 |
|          2 | HR           |      3000 |        2 |      1500 |
|          3 | SubIT        |     26000 |        4 |      6500 |
|          4 | SubSubIT     |     16000 |        2 |      8000 |
|         10 | A            |       700 |        7 |       100 |
|         11 | AB           |       100 |        1 |       100 |
|         12 | AC           |       400 |        4 |       100 |
|         13 | AD           |       100 |        1 |       100 |
|         14 | ACE          |       100 |        1 |       100 |
|         15 | ACF          |       100 |        1 |       100 |
|         16 | ACG          |       100 |        1 |       100 |
+------------+--------------+-----------+----------+-----------+

逐步、逐个 CTE 运行查询以了解其工作原理。

CTE_Departments 给出每个部门的总人数和人数。

CTE_Recursive 递归地为每个部门生成子行,同时保留 OriginalID - 递归开始的部门的 ID。

最终查询只是按此 OriginalID 对所有内容进行分组。

关于sql获取自引用表中的平均工资,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48773468/

相关文章:

java - 在一个轴和 where mdx 查询中使用相同的层次结构失败

java - 根据日期时间和持续时间列从表 t1 中选择学生在一起的行

sql - 需要在sql中获取上个月只有3个字母JUL

sql - 相当于 SQL Server 中的 PostgreSQL 类型 "timestamp without time zone"

sql - 如何管理数据库中的 "groups"?

swift - 使用 CoreAudio 在 Swift 中以编程方式创建聚合音频设备

python - groupby后聚合的列组合

mysql - 重新格式化表数据sql

sql - 在某个单词 Presto SQL 之后提取字符串

sql - "read commited"和 "repeatable read"的区别