Stored procedures, packages, functions, and triggers parsed structurally. Converted to PySpark notebooks and Spark SQL on Databricks with Delta Lake. 2,000+ built-in function mappings, full lineage, validated parity.
Upload PL/SQL, get converted code →Oracle packages bundle specification and body into monolithic database objects. MigryX decomposes them into clean Python modules with clear class structure, public APIs preserved. 350 packages decomposed across a single engagement.
Oracle's proprietary START WITH...CONNECT BY PRIOR syntax has no direct Spark equivalent. MigryX converts hierarchical queries to recursive CTEs in Spark SQL or equivalent PySpark GraphFrames logic, preserving LEVEL, SYS_CONNECT_BY_PATH, and CONNECT_BY_ROOT.
Oracle's BULK COLLECT / FORALL pattern fetches rows into PL/SQL collections for batch DML. On Databricks, this maps naturally to DataFrame batch operations that distribute across the cluster. Row-by-row cursor loops become set-based transformations.
A PL/SQL procedure using explicit cursors and BULK COLLECT for batch processing -- converted to native PySpark DataFrame operations on Databricks. No row-by-row processing, no single-node bottleneck.
-- Procedure: process_customer_segments
CREATE OR REPLACE PROCEDURE process_segments AS
TYPE t_cust IS TABLE OF customers%ROWTYPE;
l_custs t_cust;
CURSOR c_active IS
SELECT * FROM customers
WHERE status = 'ACTIVE'
AND total_spend > 1000;
BEGIN
OPEN c_active;
LOOP
FETCH c_active BULK COLLECT
INTO l_custs LIMIT 5000;
EXIT WHEN l_custs.COUNT = 0;
FORALL i IN 1..l_custs.COUNT
INSERT INTO customer_segments VALUES (
l_custs(i).cust_id,
DECODE(l_custs(i).total_spend,
NULL, 'Unknown',
'Platinum'),
NVL(l_custs(i).region, 'Unassigned'),
SYSDATE
);
COMMIT;
END LOOP;
CLOSE c_active;
END;
# PL/SQL procedure → PySpark on Databricks
from pyspark.sql import functions as F
def process_segments():
df = spark.read.table("customers")
segmented = (
df.filter(
(F.col("status") == "ACTIVE") &
(F.col("total_spend") > 1000)
)
.withColumn("segment",
F.when(F.col("total_spend").isNull(),
"Unknown")
.otherwise("Platinum"))
.withColumn("region",
F.coalesce(F.col("region"),
F.lit("Unassigned")))
.withColumn("processed_date",
F.current_date())
.select("cust_id", "segment",
"region", "processed_date")
)
segmented.write.format("delta") \
.mode("append") \
.saveAsTable("customer_segments")
process_segments()
Cursor loop with BULK COLLECT replaced by distributed DataFrame operations. DECODE mapped to F.when().otherwise(). NVL mapped to F.coalesce(). SYSDATE mapped to F.current_date(). Output writes to Delta Lake with ACID guarantees.
| Oracle PL/SQL Component | Databricks Equivalent | Notes |
|---|---|---|
| Stored Procedure | Python function | Parameters, exception handling preserved |
| Package (spec + body) | Python module | Public API as module exports, private as internal |
| CONNECT BY hierarchical query | Recursive CTE (Spark SQL) | LEVEL, SYS_CONNECT_BY_PATH preserved |
| BULK COLLECT / FORALL | DataFrame batch operations | Set-based processing replaces row batching |
| Trigger (BEFORE/AFTER) | Delta Live Tables expectations | Row-level and statement-level logic mapped |
| DECODE | F.when().otherwise() | Multi-branch DECODE fully expanded |
| NVL / NVL2 | F.coalesce() | NVL2 three-arg pattern preserved |
| Sequence | monotonically_increasing_id() | Or Delta identity columns |
| Materialized View | Delta table | Refresh logic mapped to scheduled notebook |
| DBMS_SCHEDULER jobs | Databricks Workflow | Cron triggers, dependency chains preserved |
| EXECUTE IMMEDIATE | spark.sql() | Dynamic SQL string execution mapped |
| Cursor FOR loop | DataFrame iteration | Row-by-row logic converted to set-based ops |
Data Matching compares Oracle output against Databricks output -- row by row, column by column. In the case study below, all stored procedure outputs were validated with full production backtesting against the original Oracle database.
See how Data Matching works →4,500 PL/SQL objects converted to PySpark and Spark SQL on Databricks. 350 packages decomposed into Python modules. CONNECT BY hierarchical queries rewritten as recursive CTEs. BULK COLLECT / FORALL batch processing replaced with distributed DataFrame operations. Oracle license costs eliminated within 90 days of cutover.
Read the full case study →Upload a PL/SQL package or stored procedure. Get parsed lineage, PySpark code for Databricks, and a validation report.