简体   繁体   中英

SQL - How can I get the average employee salary for each department?

This is the UML for the tables: https://dev.mysql.com/doc/employee/en/sakila-structure.html

在此处输入图片说明

This my attempt:


SELECT
    CONCAT(employees.first_name, ' ', employees.last_name) AS 'EmployeeName',
    salaries.emp_no AS 'Employee Number',
    departments.dept_no AS 'Department Number',
    departments.dept_name AS 'Department name',
    AVG(salaries.salary) AS 'Average salary'
FROM salaries
INNER JOIN dept_emp
    ON salaries.emp_no = dept_emp.emp_no
INNER JOIN employees
    ON salaries.emp_no = employees.emp_no
INNER JOIN departments
    ON dept_emp.dept_no = departments.dept_no
GROUP BY    salaries.emp_no,
            dept_emp.dept_no

I just get the average employee salary for all departments a person worked in. My desired result must group by each employee for each department. Any help is appreciated.

you can try this query

SELECT
    CONCAT(employees.first_name, ' ', employees.last_name) AS 'EmployeeName',
    salaries.emp_no AS 'Employee Number',
    departments.dept_no AS 'Department Number',
    departments.dept_name AS 'Department name',
    Cte_DepartmentSalaries.AvgSalary AS 'Average Salary'

--AVG(salaries.salary) AS 'Average salary'

FROM salaries
INNER JOIN dept_emp
    ON salaries.emp_no = dept_emp.emp_no
INNER JOIN employees
    ON salaries.emp_no = employees.emp_no
INNER JOIN departments
    ON dept_emp.dept_no = departments.dept_no   
LEFT JOIN (SELECT
    departments.dept_no,
    departments.dept_name,
    AVG(Salaries.Salary) AS AvgSalary
FROM Salaries
INNER JOIN dept_emp
    ON salaries.emp_no = dept_emp.emp_no
INNER JOIN departments
    ON dept_emp.dept_no = departments.dept_no
GROUP BY    departments.dept_no,
            departments.dept_name) AS Cte_DepartmentSalaries
    ON dept_emp.dept_no = Cte_DepartmentSalaries.dept_no

if you join to salaries where to_date between from and to date you will get any changes in the salary over those time periods.

SELECT  CONCAT(e.first_name,' ',e.last_name) AS 'EmployeeName',
        s.emp_no AS 'Employee Number',
        d.dept_no AS 'Department Number',
        d.dept_name AS 'Department name',
        AVG(s.salaries.salary) AS 'Average salary'
FROM    employees e
        JOIN dept_emp de ON e.emp_no = de.emp_no
        JOIN salaries s ON s.emp_no = de.emp_no
                           AND (de.to_date >= s.from_date AND de.to_date <= s.to_date)
        JOIN departments d ON d.dept_no = de.dept_no
GROUP BY s.emp_no,
        d.dept_no

Change your group by to

GROUP BY employees.first_name, employees.last_name,  
         salaries.emp_no, departments.dept_no, departments.dept_name

mysql lets you (in a non helpful way IMHO) not put all columns used in the group by. Unlike other platforms that force you to put them all in if they are not in the aggregate function. So you are getting more than one of the same row, but you can't tell because mysql squishes them for you.


you probably also need this in your join

INNER JOIN dept_emp ON salaries.emp_no = dept_emp.emp_no
  AND salaries.from_date = dept_emp.from_date 
  AND salaries.to_date = dept_emp.to_date 

To find just the average salaries from a table use this mySQL query.

SELECT AVG(salary), COUNT(*) 
     FROM employees;MY

SELECT AVG(salary) FROM Employees GROUP BY(departments_id);

Looks like you would need to check for an overlap in the from_date and to_date , of the employee salary history ( salaries table) and the employees department history ( dept_emp table).

Assuming that a to_date value of NULL represents "current", to find the overlap, the join would be something like this:

 FROM salaries s 
 JOIN dept_emp de
   ON ( de.emp_no    = s.emp_no )
  AND ( de.from_date <= s.to_date   OR s.to_date  IS NULL )  
  AND ( de.to_date   >= s.from_date OR de.to_date IS NULL ) 

As far as getting the average salary, that's going to be a bit more complicated, depending on how you define "average".

As an example, if an employee worked in a department and the salary was 20,000 for one year, and then salary was 30,000 for four years... should the average salary be reported as 25,000 (the average of the two distinct values), or would it be 28,000 (total salary for all years, divided by the number of years.)

To get the latter result, we could use an expression that calculates the number of days the salary was effective for in the department...

     TIMESTAMPDIFF(DAY
       ,GREATEST(de.from_date,s.from_date)
       ,LEAST(IFNULL(de.to_date,DATE(NOW())),IFNULL(s.to_date,DATE(NOW())))
     )

We could multiple that by the effective salary, add all of those together, and then divide by the total number of days:

SUM(days*salary)/SUM(days)

An expression something like this:

SUM( TIMESTAMPDIFF(DAY
       ,GREATEST(de.from_date,s.from_date)
       ,LEAST(IFNULL(de.to_date,DATE(NOW())),IFNULL(s.to_date,DATE(NOW())))
     )
*
s.salary
)
/
SUM( TIMESTAMPDIFF(DAY
       ,GREATEST(de.from_date,s.from_date)
       ,LEAST(IFNULL(de.to_date,DATE(NOW())),IFNULL(s.to_date,DATE(NOW())))
     )
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM