Queries & Pagination
The tables now exist in both worlds — the next friction point is shaping a result set. Limiting row counts, paging through results, and handling NULLs all use different keywords in SQL Server than the ones muscle memory expects from MySQL.
💡 The Bottom Line
LIMIT n becomes TOP (n), and it moves from the end of the query to
right after SELECT. The short LIMIT n OFFSET m becomes the far more
verbose OFFSET m ROWS FETCH NEXT n ROWS ONLY — and that clause only works alongside an
ORDER BY, which T-SQL requires for it. IFNULL(a, b) becomes
ISNULL(a, b) (or the standard COALESCE, which both engines support).
Everything else about SELECT/WHERE/ORDER BY reads almost
identically.
Limiting rows: LIMIT vs TOP
SELECT *
FROM orders
ORDER BY order_date DESC
LIMIT 10;
SELECT TOP (10) *
FROM Orders
ORDER BY OrderDate DESC;
📋 Key differences
LIMITsits at the very end of the query, afterORDER BY;TOPsits right afterSELECT, before the column list — a much bigger jump in position than the name change alone suggests.- SQL Server's
TOPwithout parentheses (SELECT TOP 10 *) is legal legacy syntax, but the parenthesized formTOP (10)is the modern, recommended one — MySQL'sLIMIThas no equivalent bare-vs-parenthesized distinction, it's alwaysLIMIT n. TOPcan take aPERCENTmodifier (TOP 10 PERCENT), and even aWITH TIESmodifier that includes extra rows tied with the last one returned by theORDER BY; MySQL'sLIMIThas no percent or ties form — you'd compute the row count or handle ties yourself.
Paging: LIMIT-OFFSET vs OFFSET-FETCH
SELECT *
FROM orders
ORDER BY order_date DESC
LIMIT 10 OFFSET 20;
SELECT *
FROM Orders
ORDER BY OrderDate DESC
OFFSET 20 ROWS
FETCH NEXT 10 ROWS ONLY;
Both statements return the same thing: skip the first 20 rows, return the next 10. MySQL's
LIMIT count OFFSET offset is short and to the point (MySQL also accepts a comma form,
LIMIT offset, count, which reverses the argument order and is a common source of
off-by-swap bugs even for MySQL veterans). SQL Server's ANSI-standard
OFFSET ... FETCH NEXT ... ROWS ONLY is far more verbose, but explicit about which
number is the skip and which is the page size — and, critically, it requires an ORDER BY
clause to be present at all; MySQL's LIMIT works with no ordering whatsoever (though the
result order is then technically undefined).
⚠️ Gotcha
Trying to write OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY on a query with no
ORDER BY throws a compile error in SQL Server — ORDER BY is mandatory for
OFFSET/FETCH, unlike MySQL where LIMIT happily runs (and
picks some arbitrary, storage-order-dependent set of rows) with no ORDER BY at all. If
you're porting a MySQL query that relied on an implicit "good enough" order without ever writing
one, SQL Server will force you to add an explicit ORDER BY before it lets the query
run.
NULL handling: IFNULL / COALESCE vs ISNULL
SELECT IFNULL(middle_name, '') AS middle_name
FROM users;
SELECT ISNULL(MiddleName, '') AS MiddleName
FROM Users;
📋 Key differences
IFNULL(a, b)is MySQL's two-argument NULL substitution function; SQL Server's direct equivalent is the similarly-shaped, non-standardISNULL(a, b)— note it's a completely different function from the ANSINULLIF, despite the similar name.- Both engines support the ANSI-standard
COALESCE(a, b, c, ...), which takes any number of arguments and returns the first non-NULL one — it's the more portable choice if you expect to support more than one database going forward. - Watch return-type behavior: SQL Server's
ISNULLtakes its result type from the first argument, which can cause silent truncation if the fallback value is longer than the first argument's declared type — a subtlety MySQL's more loosely-typedIFNULLdoesn't share.COALESCEon both engines determines the type from all arguments, which is usually safer.
String functions: SUBSTRING differences
SELECT SUBSTRING(email, 1, 5) FROM users;
-- also: SUBSTR(email, 1, 5)
-- also: SUBSTRING(email, -5) for the last 5 chars
SELECT SUBSTRING(Email, 1, 5) FROM Users;
-- no negative-start shortcut —
-- combine RIGHT() and LEN() instead
SUBSTRING(string, start, length) works the same way on both engines — 1-based
indexing, three arguments. The difference shows up at the edges: MySQL's convenience of a negative
start position doesn't carry over. SQL Server's SUBSTRING requires a positive starting
position and a length, always — for "last 5 characters" you'd write
RIGHT(Email, 5) instead of relying on SUBSTRING(Email, -5) the way you
could in MySQL.
🔧 Try It Yourself
Take a MySQL paging query that uses LIMIT count OFFSET offset and rewrite it for
SQL Server using OFFSET offset ROWS FETCH NEXT count ROWS ONLY — remember to add an
explicit ORDER BY if your original query didn't have one, since T-SQL won't run
without it.
🙋 There Are No Dumb Questions
Q: Does SQL Server support LIMIT at all, so I don't have to change my
queries?
A: No — SQL Server only understands TOP and the ANSI
OFFSET ... FETCH NEXT ... ROWS ONLY form. MySQL's own LIMIT/
LIMIT ... OFFSET syntax is not valid T-SQL and will throw a syntax error — this is
one of the few places SQL Server chose the more verbose standard phrasing over MySQL's shorthand.
✅ Quick Check
A MySQL developer writes SELECT * FROM Orders OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
against SQL Server, with no ORDER BY clause. What happens?
Next up: once rows are flowing, the next question is how each engine assigns and retrieves auto-generated key values for new rows — AUTO_INCREMENT and LAST_INSERT_ID() have SQL Server equivalents that behave a little differently.