Plan your migration →
Load-script ETL is still trapped inside .qvf and .qvw binaries

Convert Qlik load scripts to modern data pipelines

Parser-driven modernization of Qlik Sense .qvf apps, QlikView .qvw documents, and .qvs includes — LOAD/RESIDENT/JOIN, ApplyMap, QVD stores, and Section Access. Full lineage, automated conversion, validated parity.

MigryX analyzer showing a parsed Qlik load script as an interactive lineage graph Qlik load script parsed into interactive lineage graph
Architecture

Qlik in. Native platform out.

One source, every target. Deterministic parsers read the estate and emit native code for the platform you pick — not Qlik apps re-pointed at a cloud warehouse.

Qlik estate → MigryX parser → native platforms

Qlik
Sense apps.qvf / .qvw
Load script.qvs includes
ApplyMapLookup + join
QVD storeIntermediate files
MigryX Parser
Deterministic parseAI optional
Lineage / STTMBefore cutover
Script graphRESIDENT + JOIN
Var bindSET / LET rewrite

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

The warehouse is the system of record. The script is still the ETL.

Transformation lives inside a binary app

A .qvf or .qvw is not a reviewable pipeline. The load script, variables, and QVD paths sit inside the app. That coupling is what makes estates hard to review, test, or port when the target is Databricks, Snowflake, or BigQuery.

QVD is a proprietary intermediate layer

STORE ... INTO *.qvd is how Qlik stages data between apps. Those files do not travel to a lakehouse. Parsing the STORE and LOAD statements is how the intermediate layer becomes Delta, Iceberg, or warehouse tables instead of another QVD tree.

QlikView and Sense licenses keep compounding

QlikView estates are on a long sunset path toward Sense or Qlik Cloud. Teams that already picked a warehouse do not need another Qlik runtime just to keep last year's load script alive.

See it in action

ApplyMap, JOIN, and a filtered STORE — the script that carries the logic

A single load script doing a mapping load, a derived margin, a left join for the customer name, and a QVD store. Everything meaningful about the pipeline is inside the script, which is why exporting the app is not the same as understanding it.

Qlik load script (.qvs)
// App: LOAD_ORDER_FACT   Script: Main
SET vMarginFloor = 0;

Customers:
MAPPING LOAD
    CUST_ID,
    TIER_CODE
FROM [lib://QVD/customer.qvd] (qvd);

Orders:
LOAD
    ORDER_ID,
    CUST_ID,
    REVENUE,
    COST,
    REVENUE - COST as MARGIN,
    ApplyMap('Customers', CUST_ID, 'UNKNOWN') as TIER
FROM [lib://QVD/orders.qvd] (qvd)
WHERE REVENUE - COST >= $(vMarginFloor);

LEFT JOIN (Orders)
LOAD
    CUST_ID,
    FIRST_NM & ' ' & LAST_NM as FULL_NAME
FROM [lib://QVD/customer_dim.qvd] (qvd);

STORE Orders INTO [lib://QVD/order_fact.qvd] (qvd);
MigryX
converts
PySpark on Databricks
# ApplyMap + JOIN + STORE → DataFrames
from pyspark.sql import functions as F

customers = spark.read.table("customer")
orders = spark.read.table("orders")

enriched = (
    orders
    .join(customers.select("cust_id", "tier_code"),
          "cust_id", "left")
    .withColumn("margin", F.col("revenue") - F.col("cost"))
    .withColumn("tier",
        F.coalesce("tier_code", F.lit("UNKNOWN")))
    .filter(F.col("margin") >= 0)
)

dims = spark.read.table("customer_dim").select(
    "cust_id",
    F.concat_ws(" ", "first_nm", "last_nm")
     .alias("full_name"),
)

out_valid = enriched.join(dims, "cust_id", "left") \
    .select("order_id", "full_name", "margin", "tier")

out_valid.write.format("delta").mode("overwrite") \
    .saveAsTable("order_fact")

MAPPING + ApplyMap becomes a left join with coalesce. SET variables become parameters. STORE to QVD becomes a Delta write instead of another proprietary file.

Coverage

What gets converted

Every Qlik load-script construct in your apps maps to a defined target equivalent, recorded in the lineage report.

Qlik Construct Target Equivalent Notes
LOAD ... FROMTable / file readLib connects and file formats mapped
RESIDENTDataFrame reuse / CTEIn-memory table references preserved
JOIN / LEFT JOIN / INNER.join() / SQL JOINJoin keys and target table kept
CONCATENATEunionByName / UNION ALLColumn alignment reported
MAPPING + ApplyMapLookup join + coalesceDefault values become explicit
Peek / PreviousWindow lag / leadRow-to-row script logic set-based
IntervalMatchRange joinFrom/to keys preserved
Crosstable / Generic LOADUnpivot / pivotAttribute-value reshapes kept
STORE ... INTO .qvdTable / Delta writeQVD layer replaced, not rehosted
$(Include=) / .qvsImported moduleShared script files stay reusable
SET / LETParameters and configPer-environment values kept separate
Section AccessRBAC / catalog grantsReduction rules extracted for review
Chart / set analysisExtracted with review reportFront-end expressions reported, not guessed
Validation

Every conversion is validated to row-level parity

MigryX Data Matching compares Qlik load-script output against the new pipeline output, row by row and column by column, with configurable tolerance rules and mismatch drill-down.

See how Data Matching works →
FAQ

Qlik modernization questions

What Qlik artifacts do you need?

The .qvf (Sense) or .qvw (QlikView) apps plus any standalone .qvs include files. The load script is the substantive input. Binary app files are unpacked so the script, variables, and QVD paths can be parsed; compiled QVDs themselves are not the conversion target.

Do you convert QVD files or the load script?

The load script. QVD is a proprietary columnar store. Parsing STORE and LOAD statements produces native table reads and writes on the target, so the intermediate QVD layer disappears instead of being rehosted.

What happens to chart expressions and set analysis?

Front-end expressions are extracted and reported with the sheets that use them instead of being silently rewritten. Load-script ETL is the conversion target. Chart logic is handed back so the BI rebuild is a review, not a guess.

We are still on QlikView. Does that change anything?

No. Parsing works from the .qvw document script and any $(Include=...) .qvs files. A Qlik Cloud subscription or a running Qlik Sense engine is not required.

What to bring to a Qlik assessment

Review a representative sample with us →

Explore other modernizations

Targets: Snowflake Databricks Google Cloud Azure AWS PySpark Polars Iceberg DBT SQLMesh
Sources: SAS Alteryx Talend Qlik DataStage Informatica COBOL Oracle Teradata SSIS