Queries & Pagination
Core SELECT syntax is nearly identical between the two engines — joins, group
by, subqueries all translate almost word for word. Where Oracle developers get tripped up is
the handful of things Oracle needed a special mechanism for that SQL Server just... doesn't
need, plus a few renamed string functions.
💡 The Bottom Line
Oracle's ROWNUM pseudo-column and the one-row DUAL table both exist
to work around limitations that SQL Server simply doesn't have — SQL Server can run a
SELECT with no FROM clause at all, and has a purpose-built
OFFSET...FETCH clause for pagination. Where Oracle mostly renamed C-style string
functions (SUBSTR, INSTR), SQL Server mostly kept SQL-standard names
(SUBSTRING, CHARINDEX) — the logic is the same, only the spelling
differs.
Pagination: ROWNUM vs OFFSET/FETCH
SELECT *
FROM (
SELECT e.*, ROWNUM rn
FROM (
SELECT * FROM employees
ORDER BY hire_date DESC
) e
WHERE ROWNUM <= 20
)
WHERE rn > 10;
SELECT *
FROM employees
ORDER BY hire_date DESC
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
📋 Key differences
- Oracle's
ROWNUMis assigned beforeORDER BYis applied to the outer query, which is why the classic pattern needs a nested subquery just to paginate correctly — a very common Oracle interview gotcha in its own right. - SQL Server's
OFFSET...FETCH(added in SQL Server 2012) does exactly what it reads as: skip N rows, then take the next M — no subquery gymnastics required, and it requires anORDER BYto be meaningful. - Oracle 12c+ also added a direct equivalent,
OFFSET n ROWS FETCH NEXT m ROWS ONLY— if you're on a modern Oracle version, that syntax is now closer to SQL Server's than the classicROWNUMpattern above. - For "just give me the top N," Oracle's modern
FETCH FIRST n ROWS ONLYmaps to SQL Server'sSELECT TOP (n).
The DUAL table vs no table at all
SELECT SYSDATE FROM DUAL;
SELECT 1 + 1 FROM DUAL;
SELECT GETDATE();
SELECT 1 + 1;
Oracle's grammar requires every SELECT to have a FROM clause, so
Oracle ships a permanent one-row, one-column table called DUAL purely so you can
select a constant or call a function without a real table. SQL Server's grammar has no such
restriction — FROM is optional, so you just omit it.
⚠️ Gotcha
If you copy-paste an Oracle query into SQL Server and it still says FROM DUAL,
it will actually fail — SQL Server has no built-in DUAL table (some teams create
a fake one for compatibility, but it's not there by default). Just delete
FROM DUAL entirely; don't try to find its SQL Server equivalent, because there
isn't one — it isn't needed.
String functions: renamed, not reinvented
SELECT SUBSTR(last_name, 1, 3),
INSTR(last_name, 'a'),
LENGTH(last_name)
FROM employees;
SELECT SUBSTRING(last_name, 1, 3),
CHARINDEX('a', last_name),
LEN(last_name)
FROM employees;
📋 Key differences
SUBSTR(string, start, length)→SUBSTRING(string, start, length)— same three arguments, same order, just the SQL-standard spelling.INSTR(string, search)→CHARINDEX(search, string)— note the argument order flips: Oracle takes (haystack, needle), SQL Server takes (needle, haystack).LENGTH(string)→LEN(string)— same behavior, shorter name. One subtlety: SQL Server'sLEN()trims trailing spaces from the count; Oracle'sLENGTH()does not.- Concatenation inside these calls still follows lesson 1's rule:
||in Oracle,+orCONCAT()in SQL Server.
🔧 Try It Yourself
Take an Oracle report query that uses ROWNUM for a "top 20" list and rewrite it
as a SQL Server SELECT TOP (20) ... ORDER BY .... Then take a second query that
pages results (rows 11–20) and rewrite it using OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY.
Notice how much shorter the SQL Server version is — no nested subquery required.
🙋 There Are No Dumb Questions
Q: Does SQL Server have anything like ROWNUM I should know about?
A: The closest analog is ROW_NUMBER() OVER (ORDER BY ...), a window function that
assigns a sequential number per row based on an explicit ordering — and unlike classic Oracle
ROWNUM, it's applied to already-ordered results, so it doesn't have the
"filter-before-sort" trap that makes the classic ROWNUM pagination pattern
necessary in the first place. Oracle also has ROW_NUMBER() as an equivalent
window function, if you'd rather use the same construct on both sides.
✅ Quick Check
You want to select the literal value 42 with no table involved. In SQL Server,
what's the correct query?
Next up: how each engine auto-generates numbers — Oracle's standalone SEQUENCE
objects versus SQL Server's IDENTITY columns.