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.

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.





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 .
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
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)
Hi,
@ archana
try this. hope this will help u.
select convert(decimal(3,2),convert(decimal(4,3),1.0847))
Regards,
Neetu