Common Gotchas
The last mile of this transition isn't new syntax — it's the handful of behavioral differences that don't show up as an error message, just as wrong results. These are the ones that cost the most debugging time, because the query runs fine; it just doesn't mean what you think it means.
💡 The Bottom Line
SQL Server treats an empty string and NULL as genuinely different values;
Oracle treats an empty string as NULL — a distinction with no equivalent
setting, and no way to turn it off. SQL Server's default collation is usually
case-insensitive; Oracle string comparisons are case-sensitive by default. SQL Server
autocommits every statement unless you explicitly open a transaction; Oracle auto-starts a
transaction and expects an explicit COMMIT. And a handful of everyday functions
— starting with GETDATE() vs SYSDATE — just have different names.
Empty string vs NULL
INSERT INTO customers (name, notes)
VALUES ('Ada', '');
SELECT * FROM customers
WHERE notes IS NULL; -- does NOT match
INSERT INTO customers (name, notes)
VALUES ('Ada', '');
SELECT * FROM customers
WHERE notes IS NULL; -- matches the row above
⚠️ Gotcha
SQL Server treats an empty string as a real, distinct value with length zero — a
WHERE notes IS NULL will not match it. Oracle silently converts an empty string
literal ('') to NULL for VARCHAR2/CHAR
columns; Oracle simply has no separate concept of "a zero-length string" at the storage level
for these types. Code ported from SQL Server that relies on empty and NULL being
distinct (e.g. counting empty-string rows separately from missing-value rows) will silently
merge the two categories on Oracle — there is no session setting or column option that changes
this, it's baked into how Oracle stores character data. Any query, report, or constraint that
depends on telling '' apart from NULL needs to be rethought, not just
ported.
Case sensitivity
SELECT * FROM employees
WHERE last_name = 'smith';
-- matches 'Smith' too
SELECT * FROM employees
WHERE last_name = 'smith';
-- no match if stored as 'Smith'
📋 Key differences
- SQL Server's behavior depends entirely on the column/database collation —
most default installations use a case-insensitive collation (e.g.
SQL_Latin1_General_CP1_CI_AS, whereCImeans case-insensitive), so'smith' = 'Smith'is oftentrueout of the box. - Oracle string comparisons are case-sensitive by default, full stop — unless you enable a
case-insensitive session parameter (
NLS_SORT/NLS_COMP) or wrap both sides inUPPER()/LOWER(), which is the far more common approach in practice. - This is configurable in both directions on both engines — the point isn't that one is
"right," it's that you can't assume the SQL Server behavior carries over. Always check the
target database/column's collation rather than assuming case sensitivity either way, and
expect to add explicit
UPPER()/LOWER()calls to Oracle `WHERE` clauses that worked fine unadorned on SQL Server.
Transactions and autocommit
UPDATE employees SET salary = salary * 1.1
WHERE department_id = 10;
-- change IS already permanent
-- only needed if you opened one:
BEGIN TRAN; ... COMMIT;
UPDATE employees SET salary = salary * 1.1
WHERE department_id = 10;
-- change is NOT permanent yet
COMMIT;
-- now it is
⚠️ Gotcha
SQL Server's default is autocommit: every individual statement is its own
implicit transaction, committed the instant it succeeds. Oracle's client tools instead
implicitly start a transaction with your first DML statement, and it stays open —
uncommitted, and rollback-able — until you explicitly COMMIT or
ROLLBACK (or disconnect, which triggers an automatic rollback in many clients).
If you're used to SQL Server's autocommit and run a batch of Oracle UPDATE/
DELETE statements without a trailing COMMIT, none of it is durable
yet — a crashed client, a killed session, or simply closing your SQL*Plus/SQL Developer window
can quietly roll the whole batch back, and it will look like nothing happened at all rather
than like a partial failure.
Date/time function names
SELECT GETDATE();
SELECT DATEADD(MONTH, 3, GETDATE());
SELECT DATEDIFF(MONTH, d2, d1);
SELECT CAST(GETDATE() AS DATE);
SELECT SYSDATE FROM DUAL;
SELECT ADD_MONTHS(SYSDATE, 3) FROM DUAL;
SELECT MONTHS_BETWEEN(d1, d2) FROM DUAL;
SELECT TRUNC(SYSDATE) FROM DUAL;
📋 Key differences
GETDATE()(function call, returns server date/time) →SYSDATE(no parentheses at all — one of the more common syntax slips going this direction). For UTC, SQL Server'sSYSUTCDATETIME()/GETUTCDATE()maps to Oracle'sSYSTIMESTAMP(converted to UTC as needed).DATEADD(MONTH, n, date)→ADD_MONTHS(date, n)— note SQL Server's genericDATEADD(unit, number, date)handles days/months/years/hours all through one function by changing the first argument, where Oracle has more separate named functions (ADD_MONTHS, plus plaindate + nfor adding days).DATEDIFF(MONTH, d2, d1)→MONTHS_BETWEEN(d1, d2)— mind the argument order swap (DATEDIFF: earlier date first as the "start";MONTHS_BETWEEN: later date first).- Truncating a date to remove its time component: SQL Server's
CAST(date AS DATE)→ Oracle'sTRUNC(date), since Oracle'sDATEalways carries a time component (see lesson 1) and has no separate date-only type to cast into —TRUNCis how you zero out the time portion in place.
🔧 Try It Yourself
Find a SQL Server query in your own codebase that filters on
WHERE some_column IS NULL for a text column that might also contain empty
strings. Run the Oracle equivalent against a test table with an explicit empty-string row and
confirm the row does come back for IS NULL — a result that would have
been wrong on SQL Server. Then find one query that uses GETDATE() or
DATEADD and rewrite it with SYSDATE and ADD_MONTHS.
🙋 There Are No Dumb Questions
Q: Is there a setting to make Oracle behave like SQL Server for all of this — keep
empty strings distinct from NULL, be case-insensitive, autocommit by default?
A: Partially, and it's not recommended. Case sensitivity can be changed via
NLS_SORT/NLS_COMP session parameters. Autocommit-like behavior can be
approximated in some client tools/drivers (many JDBC/ODBC connections default to autocommit
mode even against Oracle, which surprises people the other way). But there's no supported
setting that makes Oracle store '' as a distinct, non-null value — that behavior
is fundamental to how Oracle represents character data, not a configurable session option. The
better long-term approach is to write Oracle code that matches Oracle's actual semantics rather
than trying to reproduce SQL Server's exactly.
✅ Quick Check
A developer ports a SQL Server batch script to Oracle. It runs ten UPDATE
statements in a row with no explicit COMMIT anywhere, out of habit from SQL
Server where each statement is durable the instant it succeeds. What actually happens on
Oracle?
That's the guide — time for the recap.