AI Proof Pricing Book a demo Scan your code free

Hard sources — not covered by free tools

Also parsed — certify what free tools miss

Targets

Warehouses

Runtimes

After migration

Free tools cover the SQL. We cover SAS, COBOL, Alteryx, Qlik, ODI, DataStage — and the proof. Start a pilot →

Convert Teradata BTEQ scripts to modern cloud SQL

Parser-driven modernization of BTEQ scripts and load utilities — QUALIFY, primary index design, FastLoad, MultiLoad, and TPump. Full lineage, automated conversion, validated parity.

MigryX analyzer showing parsed Teradata BTEQ scripts as an interactive lineage graph BTEQ script parsed into interactive lineage graph
Architecture

Teradata BTEQ in. Native platform out.

One source, every target. Deterministic parsers read the estate and emit native code for the platform you pick — not BTEQ replayed through a Teradata-compatible shim.

Teradata BTEQ estate → MigryX parser → native platforms

Teradata BTEQ
BTEQ scripts.bteq / SQL
FastLoadBulk ingest
MultiLoad / TPumpUpsert paths
QUALIFY + PIWindow + index
MigryX Parser
Deterministic parseAI where it helps
Lineage / STTMBefore cutover
BTEQ parseScript + SQL
QUALIFY mapNative windows

The parser is deterministic: the same input produces the same output on every run. Every output is validated against the original, row by row, before go-live.

Why now

An appliance you refresh whether you need to or not

Capacity is bought in hardware-sized steps

Growth means a node addition or a platform refresh, negotiated years ahead of the demand it serves. Warehouses that bill for compute while it runs remove that planning cycle entirely.

BTEQ scripts are batch programs disguised as SQL files

A script interleaves DDL, DML, session settings, conditional logic and error handling. No modern client runs them, so every script is a small program that has to be re-expressed rather than simply re-pointed.

The load utilities have no direct successors

FastLoad, MultiLoad, TPump and FastExport were built around Teradata's own protocols and locking. Cloud platforms load through object storage instead, which changes the shape of the job rather than just its syntax.

See it in action

A BTEQ script is a program, not a query

Session settings, conditional error handling with .IF ERRORCODE, explicit transaction boundaries and Teradata-only syntax such as QUALIFY. Re-pointing this at another database does not work, because the control flow is BTEQ's, not SQL's.

Teradata BTEQ
-- daily_sales_rank.bteq
.LOGON $TDHOST/$TDUSER,$TDPWD;
.SET WIDTH 500;
.SET ERROROUT STDOUT;

DELETE FROM sales_rank_stg ALL;
.IF ERRORCODE <> 0 THEN .QUIT 8;

INSERT INTO sales_rank_stg
SELECT  store_id,
        product_id,
        SUM(amount)  AS total_amt
FROM    sales_fact
WHERE   sale_dt BETWEEN DATE '2026-01-01' AND DATE
        '2026-01-31'
GROUP BY 1,2
QUALIFY RANK() OVER (PARTITION BY store_id
                     ORDER BY SUM(amount) DESC) <= 10;
.IF ERRORCODE <> 0 THEN .QUIT 8;

.IF ACTIVITYCOUNT = 0 THEN .GOTO NODATA;
COLLECT STATISTICS ON sales_rank_stg COLUMN(store_id);

.LABEL NODATA
.LOGOFF;
.QUIT 0;
MigryX
converts
Snowflake SQL with Python driver
# BTEQ control flow → explicit steps
import snowflake.connector

TOP_N_SALES = """
INSERT INTO sales_rank_stg
SELECT store_id, product_id, total_amt FROM (
    SELECT  store_id, product_id,
            SUM(amount) AS total_amt,
            RANK() OVER (PARTITION BY store_id
                         ORDER BY SUM(amount) DESC) AS rnk
    FROM    sales_fact
    WHERE   sale_dt BETWEEN '2026-01-01' AND '2026-01-31'
    GROUP BY store_id, product_id
) WHERE rnk <= 10;
"""

with snowflake.connector.connect(**cfg) as cx:
    cur = cx.cursor()
    cur.execute("TRUNCATE TABLE sales_rank_stg")   # DELETE ALL
    cur.execute(TOP_N_SALES)                       # QUALIFY rewritten
    if cur.rowcount == 0:                          # ACTIVITYCOUNT
        log.warning("no rows loaded; skipping stats")
    # .IF ERRORCODE handling is exception handling here;
    # COLLECT STATISTICS has no equivalent and is dropped.

QUALIFY becomes a ranked subquery with an outer filter, DELETE ALL becomes TRUNCATE, ACTIVITYCOUNT becomes a row count check, and .IF ERRORCODE becomes real exception handling. Statistics collection is dropped because the target does not need it.

Coverage

What gets converted

Every Teradata construct in your scripts maps to a defined target equivalent, recorded in the lineage report.

Teradata Construct Target Equivalent Notes
BTEQ scriptSQL steps with a driverControl flow made explicit
.IF ERRORCODE / .QUITException handling and exit codesFailure branches preserved
.LABEL / .GOTOStructured control flowJumps rewritten as conditionals
ACTIVITYCOUNTRow count checkZero-row branches kept
QUALIFY clauseRanked subquery with filterWindow function semantics preserved
PRIMARY INDEXClustering or partitioningDistribution intent mapped to target
PARTITION BY (PPI)Table partitioningRange and case partitioning translated
MULTISET / SET tablesTable with dedupe on loadSET table uniqueness enforced explicitly
Teradata-only functionsTarget dialect equivalentsOREPLACE, INDEX, ZEROIFNULL and similar
FastLoadBulk load from stageEmpty-table requirement no longer applies
MultiLoadMERGE or upsertMulti-table operations separated
TPumpStreaming or micro-batch loadRow-rate throttling no longer required
FastExportBulk unload to storageExport sessions become file writes
Stored procedure / macroStored procedure or viewMacros usually become views
COLLECT STATISTICSNot requiredTarget optimizers maintain their own statistics
Validation

Every conversion is validated to row-level parity

MigryX Data Matching compares Teradata output against the new warehouse output, row by row and column by column, with configurable tolerance rules and mismatch drill-down.

See how Data Matching works →
FAQ

Teradata modernization questions

What do you need to start?

The BTEQ scripts and DDL, plus any load utility control files such as FastLoad or MultiLoad scripts. Those files describe the batch estate. A database connection is only needed later, when validating that converted output matches.

How is QUALIFY handled on targets that do not support it?

It becomes a subquery that computes the window function and an outer filter on its result. Snowflake and Databricks now support QUALIFY directly, so on those targets the clause is often kept as-is rather than rewritten unnecessarily.

What replaces primary index and partition design?

Distribution intent is mapped to the target's own mechanism, such as clustering keys or partitioning. This is treated as a design decision rather than a syntax translation, because a primary index choice that suited Teradata's hashing is not automatically the right clustering choice elsewhere.

Do the load utilities convert automatically?

Their control files are parsed to recover the target table, the field layout and the load semantics, which then become a bulk load from object storage or a MERGE. The result is a different shape of job, since cloud platforms do not use Teradata's session-based load protocols.

What to bring to a Teradata BTEQ and SQL assessment

Review a representative sample with us →