// MigrationsConverting stored procedures between SQL dialects.
In database migrations the part that breaks estimates is not moving the data: it is moving the logic that lives inside the database. Tables and rows get copied; stored procedures must be translated, and dialect differences are not about syntax.
Why it overruns
The estimate is made on data, the cost comes from code.
- Data can be counted: tables, rows, gigabytes. Procedural code cannot — you count objects, but time depends on what they do.
- PL/SQL, T-SQL and PL/pgSQL are not variants of one language: they differ on transactions, error handling, cursors and types.
- Database logic is usually the oldest and least documented: whoever wrote it has gone, and nobody knows which edge cases it covers.
- Testing is not comparing schemas, it is comparing behaviour — and that needs real data.
Where it actually breaks
The differences that cost you are not visible by reading the code.
- Transactions
- Who opens and closes, and what happens on a mid-way error. A block that leaves a transaction open on Oracle has already rolled back elsewhere.
- NULL and empty strings
- Oracle treats the empty string as NULL, others do not. A silent difference that changes conditions without raising an error.
- Numeric and date types
- Precision, rounding and time zones differ. An invoice total can change in the last decimal, unnoticed in testing.
- Cursors and loops
- Row-by-row code that is acceptable on one engine and prohibitive on another: it converts, it works, it times out in production.
- Proprietary functions
- DECODE, NVL, analytic functions and system packages have no one-to-one equivalents: they must be rewritten, and rewritten well.
How to approach it
The order that removes uncertainty first, not the one that feels natural.
- Inventory the procedural objects before estimating: how many, how large, which are still actually called.
- Separate what converts automatically from what must be rewritten. This is where the quote is decided, not at the end.
- Convert and test in blocks, with anonymised real data: a big bang on this material cannot be controlled.
- Compare behaviour, not code: same inputs, same outputs, error conditions included.
- Keep a record of non-convertible constructs and the remedy used: it is the documentation that is always missing when the first production issue arrives.
Three costly beliefs
«It is all standard SQL anyway»
The declarative part is. Procedural logic is proprietary by definition: it exists to exploit a specific engine.
«Convert everything, then see»
Converting what nobody calls any more is wasted work. A usage inventory often cuts a large share of the objects.
«If it compiles, it works»
The worst differences raise no error: they return a different result. Without behavioural comparison you find them when the customer does.
Have a migration in the pipeline and no idea how heavy the procedural part is? Send us a sample and we will tell you what converts on its own.
Let’s talkPage updated 10 August 2026.