Friday, August 29, 2008

T-SQL: Use “Having” or “Where”

When writing T-SQL query in SQL Server, one has two options to limit the returned data. You can either use “HAVING” or “WHERE”. The question is when and how to use them?

For example:
Query1

Select tblEmployees.EmployeeID, SUM( tblEmployees.intHourThisWeek) as TotalHoursWorked
FROM tblEmployees
WHERE tblEmployees.Active=True
AND tblEmployees.DateofWork < '1/1/2008'
AND tblEmployees.DateofWork >= '1/1/2007'
GROUP BY tblEmployees.EmployeeID

and query2

Select tblEmployees.EmployeeID, SUM( tblEmployees.intHourThisWeek) as TotalHoursWorked
FROM tblEmployees
WHERE Active=True
GROUP BY tblEmployees.EmployeeID
HAVING tblEmployees.DateofWork < '1/1/2008'
AND tblEmployees.DateofWork >= '1/1/2007'

will return the same result. It summarize the total hours of each employee worked in year 2007.

But the difference is that the “WHERE” search condition is applied before the grouping operation occurs and the “HAVING” search condition is applied after the grouping operation occurs.

So if the search condition is limiting the search result into a small subset of the original table(s), it is better to apply the search condition before the grouping operation because this let the database engine to calculate the result set on a smaller subset of data.

Applying this principle to some of the queries while I am fine-tuning the database performance, it is easy to see a 60% to 75% CPU time improvement.

But you can't always move HAVING clause into WHERE clause. Because only HAVING can contain aggregate functions. HAVING clauses can reference any of the items that appear in the select list.

For example:
One company has a promotion that any salesperson who sold more than five refrigerators of one new model in the promotion date period, will qualify for a special bonus.

SELECT intSalespersonID, sum(intUnits) as totalSold
FROM tblSales
WHERE txtProduct = 'Refrigerator ABC'
--AND dateSold between @Date1 and @Date2
GROUP BY txtProduct, intSalespersonID
HAVING sum(intUnits) > 5

If you tried to move the HAVING clause to WHERE clause, you will see this error:
“An aggregate may not appear in the WHERE clause unless it is in a subquery contained in a HAVING clause or a select list, and the column being aggregated is an outer reference.”

1 comment:

ChrisRod said...

EXCELLENT summation and clear, distinct purposes of the WHERE and HAVING clauses. Keep up the fine tuning! - cdr0