Convert Oracle PL/SQL to PySpark and Spark SQL on Databricks

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 →
Why Databricks

Oracle PL/SQL was built for a different era

PL/SQL packages become Python modules

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.

CONNECT BY hierarchies become recursive CTEs and PySpark

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.

BULK COLLECT becomes Spark parallel processing

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.

Parser output

PL/SQL procedure with cursors to PySpark DataFrames

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.

Oracle PL/SQL
-- 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;
MigryX
converts
PySpark on Databricks
# 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.

Coverage

Oracle PL/SQL to Databricks — artifact mapping

Oracle PL/SQL Component Databricks Equivalent Notes
Stored ProcedurePython functionParameters, exception handling preserved
Package (spec + body)Python modulePublic API as module exports, private as internal
CONNECT BY hierarchical queryRecursive CTE (Spark SQL)LEVEL, SYS_CONNECT_BY_PATH preserved
BULK COLLECT / FORALLDataFrame batch operationsSet-based processing replaces row batching
Trigger (BEFORE/AFTER)Delta Live Tables expectationsRow-level and statement-level logic mapped
DECODEF.when().otherwise()Multi-branch DECODE fully expanded
NVL / NVL2F.coalesce()NVL2 three-arg pattern preserved
Sequencemonotonically_increasing_id()Or Delta identity columns
Materialized ViewDelta tableRefresh logic mapped to scheduled notebook
DBMS_SCHEDULER jobsDatabricks WorkflowCron triggers, dependency chains preserved
EXECUTE IMMEDIATEspark.sql()Dynamic SQL string execution mapped
Cursor FOR loopDataFrame iterationRow-by-row logic converted to set-based ops
Validation

Every conversion validated to row-level parity

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 modernized
2,000+
Function mappings
$8.5M
Savings over 3 years
350
Packages decomposed

Global Bank: Oracle PL/SQL to Databricks in 18 Months

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 →

See it on your own Oracle PL/SQL code

Upload a PL/SQL package or stored procedure. Get parsed lineage, PySpark code for Databricks, and a validation report.

Book a Live Demo → hello@migryx.com