Convert Oracle PL/SQL to BigQuery SQL

Stored procedures, packages, functions, and triggers parsed structurally. Converted to BigQuery SQL procedures and scripting. 2,000+ built-in function mappings, full lineage, validated parity.

Upload PL/SQL, get converted code →
Why BigQuery

Oracle procedural logic becomes serverless analytics

PL/SQL procedures become BigQuery scripting

BigQuery scripting supports DECLARE, SET, IF/ELSE, LOOP, and exception handling. MigryX maps Oracle's procedural blocks to BigQuery's BEGIN...EXCEPTION...END structure. Temporary tables replace PL/SQL collections. 280 packages converted in a single engagement.

CONNECT BY becomes recursive CTE natively

BigQuery has first-class support for recursive CTEs with WITH RECURSIVE syntax. MigryX converts Oracle's START WITH...CONNECT BY PRIOR hierarchical queries preserving LEVEL semantics, path construction, and cycle detection -- no workarounds needed.

Oracle types become BigQuery native types

Oracle NUMBER, VARCHAR2, DATE, CLOB, and collection types map to BigQuery INT64, STRING, DATETIME, STRING, and ARRAY/STRUCT. MigryX handles precision mapping, implicit type coercion, and Oracle-specific NULL semantics that differ in BigQuery.

Parser output

Oracle PL/SQL procedure to BigQuery SQL procedure

A PL/SQL procedure with cursor processing, DECODE, and Oracle date functions -- converted to a BigQuery SQL procedure with scripting, CASE WHEN, and native date handling.

Oracle PL/SQL
-- Procedure: refresh_department_metrics
CREATE OR REPLACE PROCEDURE refresh_metrics AS
  v_count NUMBER := 0;
  CURSOR c_depts IS
    SELECT department_id,
      NVL(department_name, 'Unassigned'),
      DECODE(status, 'A', 'Active',
        'I', 'Inactive', 'Unknown')
    FROM departments
    WHERE create_date >=
      ADD_MONTHS(SYSDATE, -6);
BEGIN
  FOR rec IN c_depts LOOP
    INSERT INTO dept_metrics (
      dept_id, dept_name, status_label,
      headcount, run_date
    )
    SELECT rec.department_id,
      rec.department_name,
      rec.status,
      COUNT(*),
      SYSDATE
    FROM employees e
    WHERE e.dept_id = rec.department_id;

    v_count := v_count + 1;
  END LOOP;

  DBMS_OUTPUT.PUT_LINE(
    'Processed: ' || TO_CHAR(v_count));
  COMMIT;
EXCEPTION
  WHEN OTHERS THEN
    ROLLBACK;
    RAISE;
END;
MigryX
converts
BigQuery SQL
-- PL/SQL procedure → BigQuery SQL
CREATE OR REPLACE PROCEDURE
  dataset.refresh_metrics()
BEGIN
  DECLARE v_count INT64 DEFAULT 0;

  INSERT INTO dataset.dept_metrics
    (dept_id, dept_name, status_label,
     headcount, run_date)
  SELECT
    d.department_id,
    COALESCE(d.department_name,
             'Unassigned'),
    CASE d.status
      WHEN 'A' THEN 'Active'
      WHEN 'I' THEN 'Inactive'
      ELSE 'Unknown'
    END AS status_label,
    COUNT(e.employee_id) AS headcount,
    CURRENT_DATE() AS run_date
  FROM dataset.departments d
  LEFT JOIN dataset.employees e
    ON e.dept_id = d.department_id
  WHERE d.create_date >=
    DATE_SUB(CURRENT_DATE(),
             INTERVAL 6 MONTH)
  GROUP BY d.department_id,
    d.department_name, d.status;

  SET v_count = (
    SELECT COUNT(DISTINCT dept_id)
    FROM dataset.dept_metrics
    WHERE run_date = CURRENT_DATE());

EXCEPTION WHEN ERROR THEN
  SELECT @@error.message;
END;

Cursor FOR loop replaced with set-based INSERT...SELECT with GROUP BY. DECODE mapped to CASE WHEN. NVL mapped to COALESCE. ADD_MONTHS mapped to DATE_SUB with INTERVAL. SYSDATE mapped to CURRENT_DATE(). Row-by-row processing eliminated.

Coverage

Oracle PL/SQL to BigQuery — artifact mapping

Oracle PL/SQL Component BigQuery Equivalent Notes
Stored ProcedureBigQuery SQL procedureBEGIN/EXCEPTION scripting structure
Package (spec + body)Dataset + routinesPackage functions become individual routines
FunctionBigQuery UDF (SQL or JS)Scalar and table-valued functions
CONNECT BY hierarchical queryWITH RECURSIVE CTENative recursive CTE support
DECODECASE WHENMulti-branch DECODE fully expanded
NVL / NVL2COALESCE() / IF()NVL2 mapped to IF with null check
TriggerScheduled query + Cloud FunctionEvent-driven logic via Pub/Sub
SequenceGENERATE_UUID() / ROW_NUMBER()Identity generation patterns mapped
Materialized ViewBigQuery Materialized ViewDirect equivalent with auto-refresh
DBMS_SCHEDULERBigQuery scheduled queryCron-based scheduling, Cloud Scheduler
EXECUTE IMMEDIATEEXECUTE IMMEDIATEDirect equivalent in BigQuery scripting
Cursor FOR loopFOR...IN (SELECT ...) / set-based SQLRow iteration or set-based conversion
Database LinkBigQuery connection / federated queryCross-source access via BigLake
Collections (TABLE/VARRAY)ARRAY / STRUCTNested types mapped to BigQuery native
Validation

Every conversion validated to row-level parity

Data Matching compares Oracle output against BigQuery output -- row by row, column by column. In the case study below, all stored procedure results were validated with full production backtesting against the original Oracle database.

See how Data Matching works →
3,800
PL/SQL objects modernized
2,000+
Function mappings
$6.8M
Savings over 3 years
280
Packages converted

Government Agency: Oracle PL/SQL to BigQuery in 15 Months

3,800 PL/SQL objects converted to BigQuery SQL procedures and UDFs. 280 packages decomposed into BigQuery routines organized by dataset. CONNECT BY hierarchical queries rewritten as recursive CTEs. DECODE/NVL patterns mapped to CASE WHEN/COALESCE. Oracle license costs eliminated, serverless compute enabled zero-infrastructure operations.

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, BigQuery SQL code, and a validation report.

Book a Live Demo → hello@migryx.com