Plan your migration →

Convert Qlik load scripts to BigQuery SQL

Qlik .qvf / .qvw apps and .qvs includes parsed structurally. Converted to BigQuery SQL with scheduled queries and Cloud Composer orchestration. Full lineage, validated parity.

Architecture

Qlik in. Google Cloud out.

Deterministic parsers read the estate and emit native Google Cloud code — not Qlik apps re-pointed at a cloud warehouse.

Qlik → MigryX parser → BigQuery + Dataform + Composer

Qlik
Sense apps.qvf / .qvw
Load script.qvs includes
ApplyMapLookup + join
QVD storeIntermediate files
MigryX Parser
Deterministic parseAI optional
Row-level parityBefore cutover
SQLX emitDataform models
DAG emitComposer
Google Cloud
BigQuerySet-based SQL
DataformGoverned models
Cloud ComposerReplaces reload
Cloud FunctionsNon-SQL tools
IAM + CLSLeast privilege
Git reposReproducible packs

AI is an optional add-on, off by default — the conversion runs end to end without it, air-gapped if your estate requires it.

Why BigQuery

Qlik load scripts were not built for serverless analytics at scale

ApplyMap should be serverless SQL

Qlik mapping tables and JOIN/RESIDENT loads run inside a provisioned engine. BigQuery executes SQL natively on a serverless, petabyte-scale engine. Complex multi-table joins that saturate a Qlik reload finish in seconds on BigQuery with zero infrastructure management.

QVD files do not belong in GCS as-is

STORE ... INTO *.qvd writes a proprietary file. BigQuery LOAD DATA and MERGE run from GCS. MigryX turns each STORE into a table write so the QVD tree is not copied into the cloud unchanged.

Scheduling needs Cloud Composer, not QMC

Qlik Management Console reload tasks provide basic scheduling with limited dependency management. Cloud Composer (managed Airflow) delivers DAG-based orchestration with retry logic, SLA monitoring, and native BigQuery operators.

Parser output

ApplyMap + JOIN to BigQuery SQL

A Qlik load script with a mapping load and conditional segmentation — converted to BigQuery SQL with CTEs and MERGE. No Qlik engine, no provisioned servers.

Qlik load script (.qvs)
// App: Customer_Revenue_Segmentation
SET vMinAmount = 1000;

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

Txns:
LOAD
    CUST_ID,
    AMOUNT,
    TOTAL_SPEND,
    ApplyMap('Tiers', CUST_ID, 'Standard') as SEGMENT
FROM [lib://QVD/transactions.qvd] (qvd)
WHERE AMOUNT > $(vMinAmount);

Seg:
LOAD
    if(TOTAL_SPEND > 50000, 'Platinum',
      if(TOTAL_SPEND > 10000, 'Gold', SEGMENT)) as SEGMENT,
    Sum(AMOUNT) as SEGMENT_REVENUE,
    Count(CUST_ID) as CUSTOMER_COUNT
RESIDENT Txns
GROUP BY SEGMENT, TOTAL_SPEND;

STORE Seg INTO [lib://QVD/customer_segments.qvd] (qvd);
MigryX
converts
BigQuery SQL
-- ApplyMap + RESIDENT → BigQuery SQL
WITH segmented AS (
    SELECT
        t.cust_id,
        CASE
            WHEN t.total_spend > 50000 THEN 'Platinum'
            WHEN t.total_spend > 10000 THEN 'Gold'
            ELSE COALESCE(c.tier_code, 'Standard')
        END AS segment,
        t.amount
    FROM `project.dataset.customer_transactions` t
    LEFT JOIN `project.dataset.customer_master` c
        ON t.cust_id = c.cust_id
    WHERE t.amount > 1000
),
agg AS (
    SELECT
        segment,
        SUM(amount) AS segment_revenue,
        COUNT(*) AS customer_count
    FROM segmented
    GROUP BY segment
)
MERGE `project.dataset.customer_segments` tgt
USING agg src
    ON tgt.segment = src.segment
WHEN MATCHED THEN UPDATE SET
    tgt.segment_revenue = src.segment_revenue,
    tgt.customer_count = src.customer_count
WHEN NOT MATCHED THEN INSERT
    (segment, segment_revenue, customer_count)
    VALUES (src.segment, src.segment_revenue,
            src.customer_count);

ApplyMap becomes LEFT JOIN + COALESCE. Nested if() becomes CASE WHEN. STORE to QVD becomes MERGE with fully-qualified table references.

Coverage

Qlik to BigQuery — artifact mapping

Qlik Construct BigQuery Equivalent Notes
MAPPING + ApplyMapLEFT JOIN + COALESCEDefault values become explicit
JOIN / LEFT JOINJOINJoin keys preserved
RESIDENT + GROUP BYGROUP BY + aggregatesSUM, COUNT, AVG, MIN, MAX preserved
WHERE / if()WHERE / CASE WHENAll predicate expressions preserved
CONCATENATEUNION ALLColumn alignment reported
Crosstable / GenericUNPIVOT / PIVOTAttribute-value reshapes kept
LOAD FROM fileLOAD DATA / external tableBulk load from GCS
STORE ... INTO .qvdTable write / MERGEQVD layer replaced
SET / LETScripting variables / paramsEnvironment configs externalized
$(Include=) / .qvsStored procedure / routineReusable scripts stay callable
Reload task chainCloud Composer DAG taskApp chaining → Airflow
Section AccessIAM + row-level securityReduction rules extracted for review
Validation

Every conversion validated to row-level parity

Data Matching compares Qlik load-script output against BigQuery output — row by row, column by column. Send a .qvs or a .qvf and we prove the rewrite before cutover.

See how Data Matching works →
Pilot input

What to send for a Qlik to BigQuery pilot

.qvf or .qvw apps

The apps that own the load script. We unpack the script, variables, and lib connects. A running Qlik engine is not required.

Standalone .qvs includes

Shared calendar, mapping, and connection scripts referenced by $(Include=...). Those stay reusable routines on BigQuery.

Sample QVD or table extracts

Enough rows to prove parity. We do not convert the QVD format; we convert the STORE/LOAD statements that produce and consume it.

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