SA0128 : Avoid using correlated subqueries. Consider using JOIN instead
Introduction
Section titled “Introduction”Correlated subqueries can cause performance issues, as they are executed for every row in the outer query.
Description
Section titled “Description”A correlated subquery can lead to performance problems in SQL Server. This issue arises because a correlated subquery is executed repeatedly for every row in the outer query. This can significantly slow down query performance, especially with large datasets, unless the optimizer transforms it into a more efficient join.
For example:
-- Example of a correlated subquerySELECT e.EmployeeID, e.NameFROM Employees eWHERE e.Salary > (SELECT AVG(Salary) FROM Salaries s WHERE s.DepartmentID = e.DepartmentID);In this example, the SELECT AVG(Salary) subquery is correlated with the outer query by e.DepartmentID . The subquery executes for each employee, leading to potential performance degradation due to repetitive calculations.
-
Increased execution time due to repeated subquery evaluations for each outer row.
-
Higher resource consumption, affecting database server performance.
`
How to fix
Section titled “How to fix”Consider using a self-join instead of a correlated subquery to improve query performance and consistency.
Follow these steps to address the issue:
1.Identify the subquery in your SQL query that is causing performance issues due to its correlation with the outer query.
2.Convert the correlated subquery into an equivalent self-join to allow SQL Server to utilize more efficient join operations such as hash or merge joins.
3.Ensure the join conditions correctly replicate the logic of the original subquery for accurate results.
For example:
-- Original correlated subquerySELECT e.EmployeeID, e.NameFROM Employees eWHERE e.Salary > (SELECT AVG(Salary) FROM Salaries s WHERE s.DepartmentID = e.DepartmentID);Can be rewritten using a self-join as follows:
-- Rewritten query using a self-joinSELECT DISTINCT e.EmployeeID, e.NameFROM Employees eJOIN ( SELECT DepartmentID, AVG(Salary) AS AvgSalary FROM Salaries GROUP BY DepartmentID) avgSalaries ON e.DepartmentID = avgSalaries.DepartmentIDWHERE e.Salary > avgSalaries.AvgSalary;The rule has a Batch scope and is applied only on the SQL script.
Parameters
Section titled “Parameters”| Name | Description | Default Value |
|---|---|---|
| IgnoreCorrelatedQueriesInsideExistsClause | Ignore correlated queries inside EXISTS clause. | yes |
Remarks
Section titled “Remarks”The rule requires Analysis Context. If context is missing, the rule will be skipped during analysis.
Effort To Fix
Section titled “Effort To Fix”1 hour per issue.
Categories
Section titled “Categories”Performance Rules, Bugs
Additional Information
Section titled “Additional Information”Example Test SQL
Section titled “Example Test SQL”SELECT DISTINCT c.LastName, c.FirstName, e.BusinessEntityIDFROM Person.Person AS c JOIN HumanResources.Employee AS eON e.BusinessEntityID = c.BusinessEntityIDWHERE 5000.00 IN (SELECT Bonus FROM Sales.SalesPerson sp WHERE e.BusinessEntityID = sp.BusinessEntityID) ;
SELECT F.custCode, F.CustLocNum, (SELECT COUNT(*) FROM DimFacilitySupplier S WHERE S.FacilityKey = F.FacilityKey) AS CntFROM #DimFacility F;
SELECT F.custCode, F.CustLocNumFROM DimFacility FWHERE F.FacilityKey = ANY (SELECT S.FacilityKey FROM DimFacilitySupplier S);
SELECT 1FROM dbo.Table_1 t1WHERE N'Hi' IN (SELECT t2.testdata1 FROM dbo.Table_2 t2 WHERE t1.testkey = t2.testkey);Analysis Results
Section titled “Analysis Results”| Message | Line | Column | |
|---|---|---|---|
| 1 | SA0128 : Avoid using correlated subqueries. Consider using JOIN instead. | 5 | 5 |
| 2 | SA0128 : Avoid using correlated subqueries. Consider using JOIN instead. | 12 | 2 |
| 3 | SA0128 : Avoid using correlated subqueries. Consider using JOIN instead. | 24 | 3 |