简体   繁体   中英

Performance issue with correlated SQL Server query

I have a query that looks like the following, it is just entirely too slow and I don't know how to speed it up. This query is currently correlated. Will a temp table to then join solve this?

SELECT 
    e.ID, e.Name
FROM
    Employees e
WHERE
    e.Salary > (SELECT AVG(e2.Salary)
                FROM Employees e2
                WHERE e2.DepartmentID = e.DepartmentID)

Please try following SQL query

with cte as (
select
    *,
    AVG(Salary) over (partition by DepartmentID) average
from employees
)
select * from cte where Salary > average

Here you will see that I used SQL Average aggregation function with Partition By clause In order to use it I preferred a SQL CTE expression

I would try moving the sub query into a join. If there is an index on the DepartmentID that includes the salary column, then it will produce the subquery result quickly and join it to the main results.

SELECT e.ID, 
       e.Name
FROM Employees e
INNER JOIN (
    SELECT DepartmentID, AVG(Salary) as AverageSalary
    FROM Employees
    GROUP BY DepartmentId
) dptAvg ON e.DepartmentID = dptAvg.DepartmentId
WHERE
    e.Salary > dptAvg.AverageSalary

It's not just the select, think of the usecase. You need to calculate the average salary just when you add or update an employee's salary, but you could need this query more often. There's no point in recalculating each time you do a read.

I would separate the second query, store the result in the Departments table (I'm assuming you have one) in a field called AvgSalary and then have the query look like:

SELECT
    e.ID,
    e.Name
FROM
    Employees e
    JOIN Departments d ON e.DepartmentID = d.DepartmentID
WHERE
    e.Salary > d.AvgSalary

At first I suggest you to try CROSS APPLY here is some more info:

SELECT  e.ID, 
        e.Name
FROM Employees e
CROSS APPLY (
    SELECT AVG(e2.Salary) as avgs
    FROM Employees e2
    WHERE e2.DepartmentID = e.DepartmentID
    ) as p
WHERE e.Salary > avgs

If you are using SQL Server 2012 and up then you can use CTE with AVG OVER

;WITH cte AS (
SELECT  e.ID, 
        e.Name,
        AVG(e.Salary) OVER (PARTITION BY e.DepartmentID ORDER BY e.ID) as avgs,
        e.Salary
FROM Employees e
)

SELECT  ID,
        Name
FROM cte 
WHERE Salary > avgs

Pre-Calculate the average salary for each department and use it in further queries.

 SELECT AVG(e2.Salary) as avgSalary,DepartmentID into #t
      FROM Employees 
      group by DepartmentID

SELECT e.ID, 
       e.Name
FROM
    Employees e
WHERE
    e.Salary > (
      SELECT avgSalary
      FROM #t e2
      WHERE e2.DepartmentID = e.DepartmentID)

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