Logical execution order: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY.
That’s why you can sort using aggregate aliases (e.g., TotalSales
) in ORDER BY.
Example 1 — Top revenue by product (DESC) with name tie-break (ASC)
SELECT ProductName,
SUM(Quantity * UnitPrice) AS TotalSales
FROM OrderItems
GROUP BY ProductName
ORDER BY TotalSales DESC, ProductName ASC;
Groups rows per ProductName, then sorts groups by TotalSales (highest first), and alphabetically for ties.
Example 2 — Average salary by department & role (DESC, then ASC)
SELECT Department,
JobTitle,
AVG(Salary) AS AvgSalary
FROM Employees
GROUP BY Department, JobTitle
ORDER BY AvgSalary DESC, Department ASC;
Multiple grouping columns are allowed. Sort directions can differ per column.
Example 3 — Keep only groups with ≥ 10 orders, then sort by count
SELECT CustomerID,
COUNT(*) AS OrderCount
FROM Orders
GROUP BY CustomerID
HAVING COUNT(*) >= 10
ORDER BY OrderCount DESC;
HAVING filters after grouping; the final list is then sorted by the aggregate alias.
✅ You can ORDER BY an alias created in the SELECT list (e.g., TotalSales).
✅ Positional sort works in many DBs: ORDER BY 2 DESC
(sort by 2nd selected column). Use sparingly for readability.
⚠️ With DISTINCT, some systems require all ORDER BY columns to appear in the SELECT list.
⚠️ NULLS order varies by DB (e.g., PostgreSQL: NULLS FIRST|LAST
). Check your engine’s behavior.