Why WHERE Runs Before SELECT

SQL Server uses logical query processing to define what each part of a query means. Understanding that order explains several errors that look unreasonable at first.

Small wooden trays arranged in a compact sequence with beans sorted into separate groups.

Reading Order Is Not Processing Order

You write SELECT first because you want to describe the result. SQL Server must still establish the input before that result makes sense. A column cannot be selected from a row source that has not been identified.

The useful simplified order is FROM, WHERE, GROUP BY, HAVING, SELECT, and ORDER BY. Joins belong with establishing the input. DISTINCT, TOP, and other clauses add detail, but this smaller sequence explains many everyday mistakes.

This is a logical description, not a stopwatch reading of engine activity. The optimizer can choose different physical operations while preserving the required meaning. An execution plan describes that implementation.

Filter Rows Before Naming the Result

Consider prices stored as whole currency units. The query below defines a small example rather than reading an existing sales table. WHERE filters the source price, and SELECT gives the calculated price a convenient name.

WITH Products AS
(
    SELECT *
    FROM (VALUES (N'Notebook', 20), (N'Pencil', 5))
         AS v(ProductName, Price)
)
SELECT ProductName, Price * 2 AS DoublePrice
FROM Products
WHERE Price * 2 > 15;

DoublePrice does not become available to WHERE in the same query block. Replacing the filter expression with that alias produces a name-resolution error. Repeating a short expression is reasonable when it keeps the query clear.

The important boundary is the query block, not the number of lines. Moving SELECT higher on the screen changes nothing. Formatting helps people, but it does not change when an alias enters scope.

Give an Expression an Earlier Home

A derived table or common table expression can expose the calculated value as an input column. The outer query can then filter that column by name. This is often easier to maintain when the expression has several parts.

WITH Priced AS
(
    SELECT ProductName, Price * 2 AS DoublePrice
    FROM (VALUES (N'Notebook', 20), (N'Pencil', 5))
         AS v(ProductName, Price)
)
SELECT ProductName, DoublePrice
FROM Priced
WHERE DoublePrice > 15;

Do not assume the common table expression creates a stored intermediate result. The optimizer can expand and rearrange it. The benefit here is a clear scope boundary, not a promise to evaluate an expression exactly once.

Separate Row Filters From Group Filters

WHERE decides which input rows participate. GROUP BY forms groups from those rows, and HAVING decides which groups remain. That distinction matters more than memorizing a diagram.

WITH Sales AS
(
    SELECT *
    FROM (VALUES (N'A', 10), (N'A', 20), (N'B', 5))
         AS v(Region, Amount)
)
SELECT Region, SUM(Amount) AS TotalAmount
FROM Sales
WHERE Amount > 5
GROUP BY Region
HAVING SUM(Amount) > 15;

Here the row filter applies before the total is calculated. Changing that filter can change the total itself. A HAVING condition on the total answers a different question from a WHERE condition on each amount.

Keep ordinary row conditions in WHERE when that expresses the intended result. Use HAVING for conditions on groups or aggregates. Writing every condition at the end makes the query harder to explain.

Order the Selected Result

ORDER BY can usually refer to a SELECT alias because that name is available at this logical stage. This is why an alias works there after failing in WHERE. The difference is deliberate.

SELECT ProductName, Price * 2 AS DoublePrice
FROM (VALUES (N'Notebook', 20), (N'Pencil', 5))
     AS v(ProductName, Price)
ORDER BY DoublePrice DESC, ProductName;

Without ORDER BY, SQL Server does not promise presentation order. A clustered index does not replace that clause. Add a stable tie-breaker when equal sort values would otherwise leave row order ambiguous.

Keep Safety Out of Predicate Order

Logical processing does not promise left-to-right evaluation of conditions inside WHERE. Do not depend on one predicate protecting another unsafe conversion. The optimizer has room to rearrange expressions.

For text that might contain invalid numbers, consider TRY_CONVERT and handle the resulting NULL explicitly. For other expressions, choose a construction that remains safe under the documented rules. Read the plan to investigate performance, and read logical scope to investigate meaning.

When a query becomes confusing, describe what enters each stage in plain words. Identify the rows, then the groups, then the projected columns. That exercise usually reveals the misplaced filter before another hint is needed.

Logical processing is not an execution timetable, it is a guide to query meaning.

This post was rewritten from scratch in September 2026. The original, published on 2008-11-09, was a short announcement about something that no longer exists. The address is the same, the subject is now something worth keeping.

Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.

Best Practices, Database, SQL Scripts, SQL Server
Previous Post
SQL SERVER – Clear Drop Down List of Recent Connection From SQL Server Management Studio
Next Post
SQL SERVER – Check Database Integrity for All Databases of Server – DBCC CHECKDB

Related Posts

4 Comments. Leave new

  • How do i round 1.0847 to 1.09 using round function in sql server 2005

    wating for a reply

    thanks in advance .

    Reply
  • 1.0847 will not round to 1.09 (1.0857 would round to 1.09).

    Regardless the function is either:

    CONVERT(DECIMAL(3,2),1.0847) = 1.08

    or

    ROUND(1.0847, 2) = 1.0800

    Reply
  • You could also do this if you wanted to get 1.09 from 1.0857:

    SELECT CONVERT(DECIMAL(3,2),CONVERT(DECIMAL(4,3),1.0847))

    Convert twice the number to make 2 + 2 = 1 :0)

    Reply
  • Hi,
    @ archana
    try this. hope this will help u.

    select convert(decimal(3,2),convert(decimal(4,3),1.0847))

    Regards,
    Neetu

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.