Work · Open-source project

sproc-to-lakehouse

A tool that reads legacy SQL Server stored procedures, decides what can move to Databricks automatically, writes the Databricks code (a dbt project and notebooks), and then proves the new code returns the same data as the old.

Everything on this page comes from running the tool on 12 synthetic stored procedures for a fictional specialty pharmacy. No client, company, drug, prescriber or patient data is used, and no number here is an estimate.

6 of 6converted procedures return the same data as SQL Server
2 of 6match when the tool’s fixes are switched off
6 of 12procedures converted with no manual steps
19/19generated notebook statements valid on Apache Spark
10/10generated dbt model builds valid on Apache Spark
41rules that classify every T-SQL construct
How it works

From stored procedure to proven Databricks code

Each procedure is parsed and every construct is classified before any code is written. Anything the tool cannot convert safely becomes a marked manual step, not a guess.

  1. T-SQL inStored procedures plus the table definitions (DDL)
  2. Analyze41 rules mark each construct: automatic, automatic with a note, or needs a person
  3. PlanOrdered steps, with fixes for what a plain translation gets wrong
  4. GenerateA dbt project and one Databricks notebook per procedure
  5. Spark checkEvery generated statement runs or resolves on Apache Spark
  6. Parity testOld and new code run on the same data; results compared row by row
The proof

Same data in, same data out

The original procedure ran on Microsoft SQL Server 2022. The generated code ran on Apache Spark 4.0.1 with Delta Lake. Both started from the same 3,576 synthetic rows across 16 tables. Every table each procedure writes was compared on row counts, per-column checksums and row-level differences by primary key.

Procedure and tableRows (SQL Server / Spark)Columns matchingChanged by the runWith fixesFixes switched off
Loading results…

“Changed by the run” counts rows the procedure inserted (+), updated (~) and deleted (−) on SQL Server. A match on rows the procedure actually changed is real evidence; a match on untouched data would prove nothing. The six partly converted procedures are not run until a person converts their manual steps.

Explore the 12 procedures

What converted, and what needs a person

Select a procedure to see its original T-SQL next to the generated dbt model and Databricks notebook, with every finding and the reason for it.

auto: converts with the same meaning auto with note: converts; a small difference is worth checking needs review: a person must decide
ProcedureVerdictFindingsNotebookParity
Loading procedures…

6 procedures convert fully. The other 6 hold 9 manual steps in total, for constructs such as cursors, dynamic SQL, transactions and error handling, where a person should decide how the logic belongs on Databricks.

Why not just run a translator

What a plain SQL translation gets wrong

The tool uses sqlglot, an open-source SQL translator, for single statements. Translated as-is, these cases fail or give different answers on Databricks. Each error was reproduced on Apache Spark; four of them are why the plain translation matched only 2 of 6 procedures.

T-SQLPlain translation on SparkWhat the tool doesProcedures
'ID-' + CAST(n AS VARCHAR)treated as numeric addition: DATATYPE_MISMATCHrewritten to CONCAT(...)06, 11
TRY_CONVERT(DATE, s, 101)becomes TO_DATE, which fails on bad input: CANNOT_PARSE_TIMESTAMPCAST(TRY_TO_TIMESTAMP(s, 'M/d/yyyy') AS DATE)03
DATEPART(ISO_WEEK, d)becomes EXTRACT(WEEKISO ...): INVALID_EXTRACT_FIELDEXTRACT(WEEK FROM d)07
UPDATE t ... FROM t JOIN sDatabricks UPDATE has no FROM: PARSE_SYNTAX_ERRORMERGE INTO, de-duplicating the source when safe02, 08, 09
TRUNCATE TABLE topen-source Delta Lake rejects it: Table does not support truncatesDELETE FROM t04, 06
flag_bit = 1BIT arrives as BOOLEAN: DATATYPE_MISMATCHflag_bit = TRUE, CAST(... AS BOOLEAN), using the table DDL03, 04, 05, 07, 10, 11, 12
ISNULL(d, '19000101')string cannot be cast to DATE: CAST_INVALID_INPUTCAST('1900-01-01' AS DATE), using the table DDL03
STUFF(s, 4, 3, '***')passed through unchanged: UNRESOLVED_ROUTINEflagged for a person (needs review)06, 11
DATEDIFF(DAY, 23:00, 01:00 next day)returns 0 on Spark; SQL Server returns 1converted with a review note04, 07
Limits

What this does not prove yet

A real Databricks workspace

Testing used open-source Apache Spark and Delta Lake on a single machine. The code has not yet run in a Databricks workspace or through a real dbt build.

Production data

Parity was tested on 3,576 seeded synthetic rows that include awkward cases (bad dates, duplicates, missing values). Real data will hold cases this data does not.

The six partly converted procedures

They are flagged with the exact steps a person must convert. Their parity is not tested until that work is done.

Constraints

Delta Lake does not enforce primary keys the way SQL Server does. Testing caught this; a migration needs explicit checks to replace them.

Moving stored procedures to Databricks?

We can assess your procedures, convert what is safe to convert, and prove the results match before anything goes live.

Book a call