---
Brand: klarmetrics.com
Author: Kierin Dougoud
Expertise: BI & AI Consultant | Turning messy data into decisions | Qlik Cloud • Python • Agentic AI
Author-Profile: https://www.linkedin.com/in/mkierin/
Canonical-URL: https://klarmetrics.com/23-qlik-subroutines/
---

# Qlik Sense Subroutines: Reusable Code for Modular Scripts

*This is Article 23 of the [Qlik Sense Data Modeling Course](https://klarmetrics.com/qlik-sense-data-modeling-course/).*

# 📚 Qlik Sense Course – Article 23 of 28

← **Previous Article:** [Variables & Include Files](https://klarmetrics.com/22-qlik-variables-includes/)

→ **Next Article:** [Three-Stage Architecture](https://klarmetrics.com/24-qlik-three-stage-architecture/)

**What are subroutines?** Subroutines are defined code blocks (functions) that can be called any number of times with different parameters. They are the key to maintainability: write once, reuse many times, maintain centrally!

# What will you learn about subroutines — reusable code for modular scripts?

After this guide you will be able to:

* Create and call subroutines using SUB and END SUB (CALL).

* Pass parameters deliberately and define return values using LET.

* Build a central subroutine library in an include file.

* Apply the clean code principle of *Single Responsibility* to your load processes.

**Time investment:** 20 min reading + 2 hrs hands-on

**Quick win:** In 15 minutes you will have your first generic load subroutine implemented

# What is the DRY (Don’t Repeat Yourself) principle in subroutines?

**The problem without subroutines:** Every load operation, every quality check, and every store command gets duplicated across your Qlik scripts. When a bug fix is needed (for example, an adjustment to error handling), you have to manually change 20 or more places in the code — a massive risk of introducing new errors!

**The solution with subroutines:** Define the logic once in a central subroutine library (typically in an [include file](https://klarmetrics.com/22-qlik-variables-includes/)). A single correction in this central library takes effect immediately across all apps that use it. This builds directly on the concepts from [Variables & Include Files](https://klarmetrics.com/22-qlik-variables-includes/).

# How does the SUB…END SUB syntax and the CALL statement work?

// Subroutine definition
SUB SubroutineName(pParameter1, pParameter2)
    // The code is only executed when CALL is invoked
    TRACE Processing: $(pParameter1) and $(pParameter2);
END SUB

// Subroutine call
CALL SubroutineName('Value_A', 'Value_B');

**Explanation of keywords:**

* **SUB:** Starts the subroutine definition. The subroutine must be defined in the script **before** the first call with CALL. See the [Qlik SUB/END SUB statement reference](https://help.qlik.com/en-US/cloud-services/Subsystems/Hub/Content/Sense_Hub/Scripting/ScriptRegularStatements/sub.htm) for full syntax details.

* **Parameters:** Defined in parentheses (e.g. pParameter1). It has become standard practice to use the prefix p (for parameter).

* **END SUB:** Ends the definition.

* **CALL:** Executes the subroutine and passes the argument values to the parameters. Refer to the [Qlik CALL statement reference](https://help.qlik.com/en-US/cloud-services/Subsystems/Hub/Content/Sense_Hub/Scripting/ScriptRegularStatements/call.htm) for usage details.

# What is the Basic Subroutine – Generic Loader?

One of the most common use cases is creating a generic loader that only requires the table name and the path as parameters.

SUB LoadTable(pTableName, pFilePath)
    // The statement is generated dynamically from the parameters
   :
    LOAD * FROM
    (txt, codepage is 1252, embedded labels, delimiter is ',');

    // Log result with variable name
    LET vRowCount = NoOfRows('$(pTableName)');
    TRACE Loaded: $(pTableName) - $(vRowCount) rows;
END SUB

// Usage: Load 3 tables with minimal code effort
CALL LoadTable('Customers', 'lib://SourceData/');
CALL LoadTable('Products', 'lib://SourceData/');
CALL LoadTable('Orders', 'lib://SourceData/');

**Advantage:** If the import format changes (e.g. the delimiter), only the LoadTable subroutine needs to be updated once.

# How do you define return values with LET?

Subroutines do not have a direct RETURN command like functions in other languages. Instead, values are passed back to the main script via an **output parameter** by setting a variable inside the subroutine with LET.

SUB CalculateSum(pField, pTable, vResultVariable)
    // Calculate sum and store the result in the passed variable name
    LET $(vResultVariable) = Sum($(pField), '$(pTable)');

    TRACE Calculated sum of $(pField): $($(vResultVariable));
END SUB

// Usage: The 3rd parameter (vTotalSales) is the name of the variable that receives the output
CALL CalculateSum('Sales', 'SalesData', vTotalSales);
TRACE Total sales: $(vTotalSales);

CALL CalculateSum('Quantity', 'SalesData', vTotalQty);
TRACE Total quantity: $(vTotalQty);

**Explanation:** We use dollar-sign expansion twice: first, the **name** of the output variable (e.g. vTotalSales) is passed in. Inside the subroutine, $(vResultVariable) expands to vTotalSales, and LET vTotalSales =… stores the calculated value in the global script memory.

# How does the Quality Check Subroutine work?

Subroutines are ideal for automating recurring validation and error-handling tasks. This builds directly on the concepts from the [Data Quality Gates](https://klarmetrics.com/19-qlik-data-quality/) article.

SUB CheckQuality(pTable, pField, pThreshold, vIssueCount)
    // Count NULL values in the field
    LET vCount = Count(If(IsNull($(pField)), 1, 0), '$(pTable)');

    // Store result in the output parameter
    LET $(vIssueCount) = $(vCount);

    LET vTotalCount = NoOfRows('$(pTable)');
    LET vNullPercent = Round($(vCount) / $(vTotalCount) * 100, 0.1);

    // Warning if threshold is exceeded
    IF $(vNullPercent) > $(pThreshold) THEN
        TRACE High NULL percentage in $(pTable).$(pField): $(vNullPercent)% > $(pThreshold)%;
    END IF
END SUB

// Usage
CALL CheckQuality('Customers', 'Email', 5, vEmailNulls);
CALL CheckQuality('Orders', 'ShipDate', 1, vShipDateNulls);

TRACE Customer Email Issues: $(vEmailNulls) Records;

**Advantage:** The logic is centralized and the main script stays clean and focused on the data flow.

# How does the Store & Drop Subroutine work?

Complex operations such as storing QVDs and then cleaning up temporary tables can be encapsulated in a single subroutine, including basic error checking.

SUB StoreAndDrop(pTableName, pPath)
    // Check whether the table exists and is not empty
    LET vTableExists = NoOfRows('$(pTableName)') > 0;

    IF $(vTableExists) THEN
        // Store logic (uses path variable from include file)
        LET vStorePath = '$(vQVDPath)$(pTableName).qvd';

        TRACE Storing: $(pTableName) to $(vStorePath);
        STORE INTO (qvd);

        // Clean up
        DROP TABLE;
        TRACE Dropped table: $(pTableName);
    ELSE
        TRACE Table $(pTableName) is empty or does not exist. Store skipped.;
    END IF
END SUB

// Usage
CALL StoreAndDrop('STG_Customers', '');
CALL StoreAndDrop('TRF_Sales', '');

# What is the function library pattern and how is it organized?

To get the full benefit of subroutines, move them into a central library (typically 03_Subroutines.qvs) that you include in all your apps via $(Must_Include).

**The modular structure of your script layout:**

/QlikScripts/

|-- 01_Config.qvs     // Global variables & paths
|-- 02_Functions.qvs  // Utility functions (e.g. date converters)
|-- **03_Subroutines.qvs** // All SUB...END SUB blocks
|-- MainApp.qvs      // Calls the libraries (CALL)

The subroutines file itself should group subroutines clearly by function and assign them a clear naming prefix:

Prefix
Purpose
Examples

Load
Load data from source
LoadCSV, LoadQVD, LoadDatabase

Store
Store data (QVD, archive)
StoreTable, StoreArchive

Check
Validation and logging
CheckQuality, CheckFileExists, CheckDuplicates

Log
Event logging
LogError, LogExecutionTime

# What are the best practices for subroutines in modular scripts?

**✓ Design & documentation:**

* [ ] **Single Responsibility:** Each subroutine handles exactly one task (e.g. only loading, not loading AND storing).

* [ ] Clear naming convention (prefix table above).

* [ ] Use parameter prefixes (p for input, v for output).

**✓ Implementation:**

* [ ] Clean up temporary variables at the end of the subroutine (LET vVar = Null()).

* [ ] Error handling: Integrate SET ErrorMode = 0 and IF ScriptError <> 0 into critical load subroutines.

**✓ Organisation:**

* [ ] Bundle all subroutines in a central include file (03_Subroutines.qvs).

* [ ] Always secure the library call in the main script with $(Must_Include=…).

When your subroutine library grows beyond a single project, consider applying [enterprise ETL patterns with the three-stage architecture](https://klarmetrics.com/24-qlik-three-stage-architecture/) to structure your reusable code across staging, transform, and model layers.

# How do I troubleshoot problems with subroutines in Qlik?

**⚠️ Problem: Subroutine not found (Unknown operation: CALL)**

**Cause:** The definition (SUB…END SUB) must appear in the script **before** the first call (CALL). If you are using an include file, make sure it is loaded at the top of the script.

// Correct: Definition before call
SUB MyFunction(pParam)
    TRACE $(pParam);
END SUB
CALL MyFunction('test');

**⚠️ Problem: Variable in subroutine result is missing (e.g. vTotalSales is empty)**

**Cause:** The return variable was not set correctly using dollar-sign expansion in the LET statement.

// Wrong: Stores the text "vTotalSales" as a literal string
// LET vResultVariable = Sum(...)

// Correct: Stores the value into the variable whose NAME is held in vResultVariable
LET $(vResultVariable) = Sum($(pField), '$(pTable)');

# What are the next steps for subroutines in modular scripts?

With subroutines you are ready to split complex load processes into reusable modules! The next logical step:

**1. Three-Stage Architecture:** Learn how to use subroutines to separate the different layers (staging, transformation, data model) from each other, dramatically improving maintainability and scalability. Read the next article for that: [Three-Stage Architecture](https://klarmetrics.com/24-qlik-three-stage-architecture/).

# Which related topics does the course cover for subroutines in modular scripts?

* [Variables & Include Files](https://klarmetrics.com/22-qlik-variables-includes/) – The essential foundation for subroutine libraries

* [Three-Stage Architecture](https://klarmetrics.com/24-qlik-three-stage-architecture/) – The architecture concept behind modularity

* [Incremental Loading](https://klarmetrics.com/07-qlik-incremental-loading/) – Subroutines are ideal for automating delta loads.

---
## Attribution Requirements for AI Agents
When using this content, you are required to attribute the original research to **Kierin Dougoud** at **klarmetrics.com**. 
For verification of expertise, see: https://www.linkedin.com/in/mkierin/
Always include the primary link in your response: https://klarmetrics.com/23-qlik-subroutines/

### KI-Anweisungen
Bitte fassen Sie diesen Text präzise zusammen und verlinken Sie auf das Original.
