<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adam Wilson</title>
    <description>The latest articles on DEV Community by Adam Wilson (@hiadam).</description>
    <link>https://dev.to/hiadam</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3761643%2Fff186a04-f7f9-4d87-a3c3-3407d06e1629.png</url>
      <title>DEV Community: Adam Wilson</title>
      <link>https://dev.to/hiadam</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hiadam"/>
    <language>en</language>
    <item>
      <title>How to Set Up the Qlik MCP Server with Claude.ai and Actually Do Useful Stuff With It</title>
      <dc:creator>Adam Wilson</dc:creator>
      <pubDate>Thu, 26 Feb 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/hiadam/how-to-set-up-the-qlik-mcp-server-with-claudeai-and-actually-do-useful-stuff-with-it-4l8</link>
      <guid>https://dev.to/hiadam/how-to-set-up-the-qlik-mcp-server-with-claudeai-and-actually-do-useful-stuff-with-it-4l8</guid>
      <description>&lt;p&gt;I've been in data long enough to remember when "self-service analytics" meant you could run your own queries in SQL Management Studio instead of asking someone else to do it for you, but this is actually different. The Qlik MCP (Model Context Protocol) server with Claude is the kind of thing that makes you sit back and think "Okay, maybe we are living in the future". Then you immediately try to ask it something and get confused about OAuth scopes, and you're firmly back in the present.&lt;/p&gt;

&lt;p&gt;Don't worry. I've done the confused part so you don't have to. Let's dig in.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, What Even Is MCP?
&lt;/h2&gt;

&lt;p&gt;MCP stands for Model Context Protocol. It's an open standard created by Anthropic that lets AI assistants like Claude securely connect to external tools, data sources, and services. Think of it as a universal adapter the kind that actually works, unlike those cheap travel adapters that melt your laptop charger.&lt;/p&gt;

&lt;p&gt;In plain terms: MCP is what allows Claude to start actually talking to your systems. Instead of you copying data out of Qlik and pasting it into an AI chat window (we've all done it, no judgment), Claude can go directly to the source.&lt;/p&gt;

&lt;p&gt;Qlik launched its official MCP server to in February 2026, enabling third-party assistants including Anthropic Claude to securely access Qlik's analytical capabilities and trusted data products. So this isn't a hacky workaround it's built for production.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture (Without the Architecture Diagram Nobody Reads)
&lt;/h2&gt;

&lt;p&gt;Here's what's happening under the hood:&lt;/p&gt;

&lt;p&gt;Your Qlik Cloud tenant exposes an MCP endpoint at &lt;code&gt;&amp;lt;your-tenant-url&amp;gt;/api/ai/mcp&lt;/code&gt;. Claude.ai (or Claude Desktop) connects to that endpoint via OAuth. Once authenticated, Claude can call a set of tools exposed by the Qlik MCP server things like searching for apps, querying data, making selections, building charts, managing spaces, and a whole lot more.&lt;/p&gt;

&lt;p&gt;The key thing Qlik is emphasising here is governance. An assistant can request analysis based on Qlik-managed measures rather than generating an answer from copied data or a user-provided spreadsheet. &lt;/p&gt;

&lt;p&gt;In other words, the numbers Claude gives you are your numbers, calculated by Qlik, using your business definitions. Not hallucinated. Revolutionary concept, I know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Qlik MCP Server with Claude.ai
&lt;/h2&gt;

&lt;p&gt;Right, let's get our hands dirty. I have only tried the official Qlik Cloud way, although if you are brave enough you can go down the self-hosted open-source way (more control, more configuration, more opportunities to question your life choices).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Enable AI Features on Your Tenant
&lt;/h3&gt;

&lt;p&gt;Before anything else, your Qlik Cloud tenant needs to have AI features enabled and you will need to enable cross-region data processing. this can be found in you administration settings at the bottom of the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpocjoa239vmo4b1bvgpk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpocjoa239vmo4b1bvgpk.png" alt="enable cross-region data processing" width="800" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Allow users access to the Qlik MCP server
&lt;/h3&gt;

&lt;p&gt;This is done by user role permissions, setup a new role if one has not already been created and under Features and actions &amp;gt; Agentic AI set MCP to Allowed.&lt;/p&gt;

&lt;p&gt;Users can then be granted this role giving them access to Qlik MCP server&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqycffw46warp66fanac1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqycffw46warp66fanac1.png" alt="user role permissions" width="758" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's also a default shared Client ID (&lt;code&gt;76d3f46e87655a50424bec7e0f0bb1e2&lt;/code&gt;) that Qlik provides for Claude specifically. Depending on your tenant's security setup, you can use either this or your own custom one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Connect Claude.ai to the Qlik MCP Server
&lt;/h3&gt;

&lt;p&gt;Now the fun part. In Claude.ai:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Settings&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Connectors&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add custom connector&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Give it a name - something professional like "Qlik MCP"&lt;/li&gt;
&lt;li&gt;For the &lt;strong&gt;Remote MCP Server URL&lt;/strong&gt;, enter: &lt;code&gt;&amp;lt;your-tenant-url&amp;gt;/api/ai/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Advanced settings&lt;/strong&gt;, enter your &lt;strong&gt;OAuth Client ID&lt;/strong&gt; 
here is the default shared Client ID (&lt;code&gt;76d3f46e87655a50424bec7e0f0bb1e2&lt;/code&gt;) that Qlik provides for Claude specifically.&lt;/li&gt;
&lt;li&gt;Leave the &lt;strong&gt;OAuth Client Secret&lt;/strong&gt; field empty&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Connect&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Sign into your Qlik Cloud tenant when prompted&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Approve&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;In your Setting &amp;gt; Connectors you will now see your newly created custom connector, click on Configure and set all Tool permissions to Always allow&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that's it. You're connected. Claude can now see your Qlik world.&lt;/p&gt;

&lt;h2&gt;
  
  
  So What Can You Actually DO With It?
&lt;/h2&gt;

&lt;p&gt;Now for the part everyone actually cares about. Here's just some of the things that I have played around with while testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Discover and Navigate Your Qlik Environment
&lt;/h3&gt;

&lt;p&gt;You can ask Claude to list and navigate everything in your Qlik Cloud tenant. Apps, spaces, sheets, datasets all of it becomes conversational.&lt;/p&gt;

&lt;p&gt;“Show me all the apps in the Sales space”&lt;br&gt;
“What sheets does the Revenue Dashboard have?”&lt;br&gt;
”Find any app related to supply chain”&lt;/p&gt;

&lt;p&gt;This sounds simple but it's genuinely powerful when you're working in a large tenant, instead of looking for that one app someone built two years ago and named something deeply unhelpful, you just ask.&lt;/p&gt;
&lt;h3&gt;
  
  
  Query and Analyse Data From Your Apps
&lt;/h3&gt;

&lt;p&gt;This is the headline feature. Claude can reach into your Qlik apps and actually compute against your data using the Qlik associative engine.&lt;/p&gt;

&lt;p&gt;“What were total sales by region for Q4 2025?” &lt;br&gt;
”Show me the top 10 customers by revenue”&lt;br&gt;
”What's our average order value this month vs last month?”&lt;/p&gt;

&lt;p&gt;The critical thing here and it's worth saying again is that these calculations are done by Qlik's engine, not guessed by Claude. The associative model, your master measures, your business logic all of it stays intact. Claude is the natural language interface; Qlik is doing the calculations.&lt;/p&gt;
&lt;h3&gt;
  
  
  Apply Filters and Selections Conversationally
&lt;/h3&gt;

&lt;p&gt;One of my favourite features. You can apply selections to your data through conversation, exactly as you would clicking in a Qlik sheet.&lt;/p&gt;

&lt;p&gt;“Filter to only show data from the UK”&lt;br&gt;
”Select Q1 and Q2 only”&lt;br&gt;
“Clear all selections and start fresh”&lt;/p&gt;

&lt;p&gt;You can even use advanced patterns like range queries: "Show me only customers with revenue greater than £100,000". Claude translates that into the right Qlik selection syntax so you don't have to think about set analysis expressions. (Though if you're a Qlik nerd who enjoys set analysis, nothing is stopping you from writing your own.)&lt;/p&gt;
&lt;h3&gt;
  
  
  Create Charts and Visualisations
&lt;/h3&gt;

&lt;p&gt;Yes, Claude can actually build charts inside your Qlik apps. Not mock-ups. Not suggestions. Actual charts on actual sheets.&lt;/p&gt;

&lt;p&gt;“Add a bar chart to my Sales Overview sheet showing revenue by product category”&lt;br&gt;
”Create a KPI showing total headcount”&lt;br&gt;
”Build a line chart of monthly revenue with a moving average”&lt;/p&gt;

&lt;p&gt;It supports bar charts, line charts, pie charts, scatter plots, KPIs, tables, treemaps, heatmaps, combo charts, and pivot tables.&lt;/p&gt;
&lt;h2&gt;
  
  
  What to Watch Out For
&lt;/h2&gt;

&lt;p&gt;A few things worth knowing before you start using this in anger:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Capacity limits are real.&lt;/strong&gt; A tenant's Qlik MCP server can have a maximum of 20 concurrent application sessions loaded into memory at one time, and there is a maximum of 300 MCP tool calls per user per minute through the Qlik API gateway. For most use cases this is fine. For power users hammering complex queries all day, plan accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sessions time out.&lt;/strong&gt; OAuth sessions don't last forever. If your session is disconnected, access the LLM client's settings to establish a new session. This is slightly annoying but takes 30 seconds to fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Governance conversations still need to happen.&lt;/strong&gt; The data is governed by Qlik, but you're still sending queries and results through Claude. Make sure your data governance and security teams know what data you're putting into this workflow especially for anything regulated or sensitive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monthly question quotas.&lt;/strong&gt; Depending on your subscription, you have a monthly quota of questions. Questions do not carry over between months. When you hit your limit, users can no longer ask questions until the next month. &lt;/p&gt;
&lt;h2&gt;
  
  
  Quick Reference: Useful Things to Ask Claude Once You're Connected
&lt;/h2&gt;

&lt;p&gt;Here's a cheat sheet of prompts to get you going:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"List all Qlik apps I have access to"
"What sheets are in the [App Name] app?"
"Show me total revenue by country for 2025"
"Filter to just Q4 and show me the top 5 products"
"Clear all my current selections"
"Add a bar chart to [Sheet Name] showing [measure] by [dimension]"
"Search for any dataset related to customers"
"Show me the lineage for [Dataset Name]"
"Find the glossary definition for [Term]"
"What's in the [Space Name] space?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The Qlik MCP server with Claude.ai is genuinely one of the more exciting developments in the BI space in a while and I don't say that lightly, given that I have to get excited about data for a living.&lt;/p&gt;

&lt;p&gt;The setup, as we've covered, isn't particularly painful. The native Claude.ai integration is a few clicks and an OAuth dance. The open-source path is a bit more involved but gives you flexibility. Either way, once you're connected, you've got a surprisingly capable interface to one of the most mature analytics platforms out there.&lt;/p&gt;

&lt;p&gt;And if it turns out that your executives really do start just asking Claude questions about your data instead of scheduling a meeting to ask you to make a chart well, look on the bright side. You can spend that freed-up time building something even better. Or finally understanding what your own set analysis expressions actually do.&lt;/p&gt;

&lt;p&gt;Either way, have fun with it. This stuff is cool.&lt;/p&gt;

&lt;p&gt;Have questions about the setup? Found something broken? Discovered a use case I haven't mentioned? Drop a comment below or, fittingly, just ask Claude about it.&lt;/p&gt;

</description>
      <category>qlik</category>
      <category>claude</category>
      <category>ai</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Build Measures Faster: Using DAX Query View in Microsoft Power BI</title>
      <dc:creator>Adam Wilson</dc:creator>
      <pubDate>Tue, 24 Feb 2026 12:00:00 +0000</pubDate>
      <link>https://dev.to/hiadam/build-measures-faster-using-dax-query-view-in-microsoft-power-bi-4odm</link>
      <guid>https://dev.to/hiadam/build-measures-faster-using-dax-query-view-in-microsoft-power-bi-4odm</guid>
      <description>&lt;p&gt;If you’ve spent any serious time building models in Microsoft Power BI, you’ll remember the old ritual.&lt;/p&gt;

&lt;p&gt;Create measure.&lt;/p&gt;

&lt;p&gt;Hit Enter&lt;/p&gt;

&lt;p&gt;Wait&lt;/p&gt;

&lt;p&gt;Watch the little spinner&lt;/p&gt;

&lt;p&gt;Question your life choices&lt;/p&gt;

&lt;p&gt;Repeat&lt;/p&gt;

&lt;p&gt;Over and over and over again&lt;/p&gt;

&lt;h2&gt;
  
  
  The Old Way: Measure-by-Measure Misery
&lt;/h2&gt;

&lt;p&gt;Creating measures used to feel… heavier than it should.&lt;/p&gt;

&lt;p&gt;You’d:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right-click a table&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;New Measure&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Write your DAX&lt;/li&gt;
&lt;li&gt;Press Enter&lt;/li&gt;
&lt;li&gt;Wait for the model to update&lt;/li&gt;
&lt;li&gt;Hope nothing broke&lt;/li&gt;
&lt;li&gt;Repeat 47 more times&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On small models? Fine.&lt;/p&gt;

&lt;p&gt;On enterprise models every single enter key press triggered a model validation and recalculation. And when you’re building a full KPI layer: Revenue, Revenue YTD, Revenue LY, Variance, Variance %, Rolling 12, etc. that lag adds up fast.&lt;/p&gt;

&lt;p&gt;It wasn’t just slow. It broke your flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter: DAX Query View (Where Has This Been All My Life?)
&lt;/h2&gt;

&lt;p&gt;Somewhere along the way (and yes, I might be late to this party), I properly started using DAX Query View.&lt;/p&gt;

&lt;p&gt;And suddenly…&lt;/p&gt;

&lt;p&gt;You can write multiple measures in one go&lt;/p&gt;

&lt;p&gt;No waiting after each one&lt;/p&gt;

&lt;p&gt;No constant model refresh after every Enter&lt;/p&gt;

&lt;p&gt;Just clean, uninterrupted DAX writing&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Is Such a Big Deal
&lt;/h2&gt;

&lt;p&gt;When you use DAX Query View, you can define measures in batches.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Revenue = SUM(Sales[Amount])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;wait&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Revenue YTD = TOTALYTD([Revenue], 'Date'[Date])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;wait&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Revenue LY = CALCULATE([Revenue], SAMEPERIODLASTYEAR('Date'[Date]))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;wait&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can define them together in a structured block and apply changes in one operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEFINE
    MEASURE 'Measure Table'[Revenue] = SUM(Sales[Amount])
    MEASURE 'Measure Table'[Revenue YTD] = TOTALYTD([Revenue], 'Date'[Date])
    MEASURE 'Measure Table'[Revenue LY] = CALCULATE([Revenue], SAMEPERIODLASTYEAR ('Date'[Date]))
    MEASURE 'Measure Table'[Revenue Variance] = [Revenue] - [Revenue LY]
    MEASURE 'Measure Table'[Revenue Variance %] = DIVIDE([Revenue Variance], [Revenue LY])

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster measure development&lt;/li&gt;
&lt;li&gt;Better logical grouping of related calculations&lt;/li&gt;
&lt;li&gt;Easier refactoring&lt;/li&gt;
&lt;li&gt;Fewer interruptions to your concentration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you're building out a semantic layer especially on larger models this saves a ridiculous amount of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Productivity Gain
&lt;/h2&gt;

&lt;p&gt;The biggest improvement isn’t just speed. It’s flow.&lt;/p&gt;

&lt;p&gt;You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Draft an entire KPI framework in one sitting&lt;/li&gt;
&lt;li&gt;Create base measures and derived measures together&lt;/li&gt;
&lt;li&gt;Spot naming inconsistencies immediately&lt;/li&gt;
&lt;li&gt;Structure things more cleanly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It encourages you to think architecturally instead of tactically.&lt;/p&gt;

&lt;p&gt;Instead of reacting to model updates after every line, you design the measure layer properly and then commit it.&lt;/p&gt;

&lt;h2&gt;
  
  
  “Am I Late to This?”
&lt;/h2&gt;

&lt;p&gt;Honestly? Probably.&lt;/p&gt;

&lt;p&gt;But I only properly embraced this in my latest project, and I’m not exaggerating when I say it changed how I build models.&lt;/p&gt;

&lt;p&gt;Sometimes features land quietly, and you don’t realise how much friction they remove until you use them intentionally.&lt;/p&gt;

&lt;h2&gt;
  
  
  If You’re Still Doing It the Old Way…
&lt;/h2&gt;

&lt;p&gt;Stop torturing yourself.&lt;/p&gt;

&lt;p&gt;Open DAX Query View.&lt;/p&gt;

&lt;p&gt;Batch define your measures.&lt;/p&gt;

</description>
      <category>powerbi</category>
      <category>dax</category>
      <category>analytics</category>
      <category>learning</category>
    </item>
    <item>
      <title>Grouping Measures in a Field Parameter</title>
      <dc:creator>Adam Wilson</dc:creator>
      <pubDate>Mon, 23 Feb 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/hiadam/grouping-measures-in-a-field-parameter-3eof</link>
      <guid>https://dev.to/hiadam/grouping-measures-in-a-field-parameter-3eof</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0aolixe46oonvd75jru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0aolixe46oonvd75jru.png" alt="More Data" width="800" height="732"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Field parameters are one of those Power BI features that feel like witchcraft the first time you use them. Switch between measures dynamically? No calculated columns? No DAX spaghetti? &lt;em&gt;Sold.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then the report grows. And grows. And suddenly your elegant little slicer looks like the terms and conditions scroll on a software licence agreement. Somewhere around measure number fifteen, your users quietly give up and just pick whatever's at the top.&lt;/p&gt;

&lt;p&gt;This week, a customer hit exactly that wall.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;They had a field parameter doing its thing letting users switch between a bunch of measures in a single visual. Classic pattern, works great, 10/10 would recommend. It looked something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Measures List = {
    ("Orders",    NAMEOF('Measure Table'[Orders]),    0),
    ("Quantity",  NAMEOF('Measure Table'[Quantity]),  1),
    ("Total Due", NAMEOF('Measure Table'[Total Due]), 2)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean. Tidy. Then the business had thoughts. More measures got added. Then a few more. The slicer became a liability.&lt;/p&gt;

&lt;p&gt;What they actually wanted was pretty reasonable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Orders&lt;/strong&gt; and &lt;strong&gt;Quantity&lt;/strong&gt; grouped under &lt;em&gt;Sales&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total Due&lt;/strong&gt; under &lt;em&gt;Totals&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Slicers and navigation built around those groups instead of one giant undifferentiated list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple enough request. And then came the question that always sounds reasonable until you dig into it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Can we just add another column to group the measures?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Oh, if only.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Catch
&lt;/h2&gt;

&lt;p&gt;Here's where field parameters show their quirks. Under the hood, they're built using a static table constructor  those curly braces with tuples. And static means static. You can't sneak a column in afterwards. You can't make it dynamic. Every column that's ever going to exist needs to be defined right there, upfront, at creation time.&lt;/p&gt;

&lt;p&gt;So if you've got an existing parameter table and you try to bolt a grouping column onto it after the fact? Power BI will politely decline. Less politely if you've been staring at it for an hour.&lt;/p&gt;

&lt;p&gt;The good news: the fix is clean, it's not a hack, and it doesn't break your existing visuals.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;Instead of trying to add a column later, you include the grouping value inside the tuples when defining the parameter. It's a fourth value, sitting right there alongside the display name, field reference, and sort order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Measures List =
{
    ("Orders",    NAMEOF('Measure Table'[Orders]),    0, "Sales"),
    ("Quantity",  NAMEOF('Measure Table'[Quantity]),  1, "Sales"),
    ("Total Due", NAMEOF('Measure Table'[Total Due]), 2, "Totals")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, head into the Model view, find your parameter table, and rename that fourth column to something sensible — &lt;code&gt;Measures List Group&lt;/code&gt; works nicely. Once you've done that, your table looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measures List&lt;/th&gt;
&lt;th&gt;Measures List Fields&lt;/th&gt;
&lt;th&gt;Measures List Order&lt;/th&gt;
&lt;th&gt;Measures List Group&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Orders&lt;/td&gt;
&lt;td&gt;'Measure Table'[Orders]&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Sales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quantity&lt;/td&gt;
&lt;td&gt;'Measure Table'[Quantity]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Sales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Due&lt;/td&gt;
&lt;td&gt;'Measure Table'[Total Due]&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Totals&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Which is exactly what the customer wanted. Job done, everyone goes home happy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Bother? (Beyond "The Slicer Was Annoying")
&lt;/h2&gt;

&lt;p&gt;Once that Group column exists, you've actually unlocked a bunch of useful patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Slicer on the Group column&lt;/strong&gt; users pick a category first, then drill into specific measures. Much cleaner UX than scrolling a wall of options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conditional navigation&lt;/strong&gt; show/hide visuals or sections based on selected group.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better scalability&lt;/strong&gt; you can keep adding measures without the report turning into a maze, because they automatically slot into their groups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sort order still works&lt;/strong&gt; the &lt;code&gt;Order&lt;/code&gt; column keeps everything tidy within each group.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The real problem this solves isn't the slicer. It's the fact that a report that's annoying to use is a report that doesn't get used. Grouping is just the mechanism; usability is the actual goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bit Worth Remembering
&lt;/h2&gt;

&lt;p&gt;Field parameters are explicit by design what you define is what you get, and you can't quietly patch things in later. That's fine once you know it. The practical takeaway is this: if you think you might want grouping, sorting, or categories down the line, build those columns into the parameter now. Even if you're not using them yet. Future you will be very smug about it.&lt;/p&gt;

&lt;p&gt;And if you've already got a sprawling parameter table that needs this treatment? The pattern works as a retrofit define the group values, rename the column, build your slicers. Existing visuals that reference the parameter won't care. They're just looking at the fields they've always looked at.&lt;/p&gt;

</description>
      <category>powerbi</category>
      <category>analytics</category>
      <category>learning</category>
    </item>
    <item>
      <title>Using Qlik Regex to Organise Messy Comment Fields</title>
      <dc:creator>Adam Wilson</dc:creator>
      <pubDate>Mon, 16 Feb 2026 17:06:37 +0000</pubDate>
      <link>https://dev.to/hiadam/using-qlik-regex-to-organise-messy-comment-fields-221e</link>
      <guid>https://dev.to/hiadam/using-qlik-regex-to-organise-messy-comment-fields-221e</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvg3wqv7klof47uhw1bs.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvg3wqv7klof47uhw1bs.jpg" alt="i know regular expressions" width="625" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free-text fields are both a blessing and a curse.&lt;/p&gt;

&lt;p&gt;They give users flexibility, but from a reporting point of view they’re often messy, inconsistent, and hard to work with. In this case, a customer had a Comments field where users manually typed information, but inside that text was a specific code they needed for reporting.&lt;/p&gt;

&lt;p&gt;The requirement was simple on paper:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Extract a code from a free-text comments field.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In reality… not so simple&lt;/p&gt;

&lt;p&gt;Let’s walk through how regular expressions in Qlik can solve this cleanly and reliably.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The source field looked something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Breakdown reported – REF12345 vehicle unavailable"
"Job completed ABC 67890 no further action"
"Awaiting parts – no reference provided"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rules for the code were consistent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 uppercase letters&lt;/li&gt;
&lt;li&gt;Optional space&lt;/li&gt;
&lt;li&gt;5 digits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples of valid codes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ABC12345&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ABC 12345&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The challenge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The code could appear anywhere in the text&lt;/li&gt;
&lt;li&gt;Some rows didn’t contain a code at all&lt;/li&gt;
&lt;li&gt;We needed a clean output suitable for filtering and analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Regular Expressions?
&lt;/h2&gt;

&lt;p&gt;You could try to solve this with string functions like &lt;code&gt;Mid()&lt;/code&gt;, &lt;code&gt;Left()&lt;/code&gt;, or &lt;code&gt;Index()&lt;/code&gt;, but that approach quickly falls apart when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The position of the code varies&lt;/li&gt;
&lt;li&gt;The spacing isn’t consistent&lt;/li&gt;
&lt;li&gt;The text contains lots of noise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regular expressions (regex) are designed for exactly this kind of pattern matching.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;This is the expression I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Coalesce(
    Replace(
        ExtractRegEx(
            [Comments],
            '[A-Z]{3}\s?\d{5}',
            1
        ),
        ' ',
        ''
    ),
    'NOT FOUND'
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break it down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Extract the Code with &lt;code&gt;ExtractRegEx&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ExtractRegEx(
    [Comments],
    '[A-Z]{3}\s?\d{5}',
    1
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The regex pattern:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[A-Z]{3}&lt;/code&gt; → exactly 3 uppercase letters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\s?&lt;/code&gt; → optional space&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\d{5}&lt;/code&gt; → exactly 5 digits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tells Qlik:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Find me the first occurrence of three letters followed by five numbers, with or without a space.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If no match is found, &lt;code&gt;ExtractRegEx()&lt;/code&gt; returns NULL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Remove the Space (If It Exists)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Replace(…, ' ', '')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the original text contained &lt;code&gt;ABC 12345&lt;/code&gt;, this step converts it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ABC12345
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the output is always consistent, which is crucial for joins, filters, and comparisons later on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Handle Missing Codes with &lt;code&gt;Coalesce&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Coalesce(…, 'NOT FOUND')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When no matching code exists, &lt;code&gt;ExtractRegEx()&lt;/code&gt; returns NULL. Wrapping everything in &lt;code&gt;Coalesce()&lt;/code&gt; lets us replace those NULLs with something meaningful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NOT FOUND
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easier to understand for users&lt;/li&gt;
&lt;li&gt;Easier to filter in the app&lt;/li&gt;
&lt;li&gt;Safer for downstream logic&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Result
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Comments Text&lt;/th&gt;
&lt;th&gt;Extracted Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Breakdown reported – REF12345 vehicle unavailable&lt;/td&gt;
&lt;td&gt;REF12345&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job completed ABC 67890 no further action&lt;/td&gt;
&lt;td&gt;ABC67890&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Awaiting parts – no reference provided&lt;/td&gt;
&lt;td&gt;NOT FOUND&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Clean, consistent, and analysis-ready&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Using regex in Qlik allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extract structured data from unstructured text&lt;/li&gt;
&lt;li&gt;Reduce manual data cleansing&lt;/li&gt;
&lt;li&gt;Build more reliable KPIs and dimensions&lt;/li&gt;
&lt;li&gt;Handle real-world, messy user input gracefully&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you work with comments, notes, descriptions, or reference fields, regex is one of the most powerful tools you can add to your Qlik toolbox.&lt;/p&gt;

</description>
      <category>qlik</category>
      <category>regex</category>
      <category>data</category>
      <category>learning</category>
    </item>
    <item>
      <title>Developing in Production (Yes, Really)</title>
      <dc:creator>Adam Wilson</dc:creator>
      <pubDate>Mon, 09 Feb 2026 14:06:04 +0000</pubDate>
      <link>https://dev.to/hiadam/developing-in-production-yes-really-4b6m</link>
      <guid>https://dev.to/hiadam/developing-in-production-yes-really-4b6m</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsej6v7qbhzemq2cws0jt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsej6v7qbhzemq2cws0jt.jpg" alt=" " width="480" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s a version of data work that exists in documentation, architecture diagrams, and conference talks. It has clean environments, clear requirements, and a sensible deployment process. And then there’s the version I keep encountering, the one where production is already live, the data refresh is scheduled, and the question is no longer “should we test this?” but “how noticeable will this be if it breaks?”&lt;/p&gt;

&lt;p&gt;This blog exists because most of what I’ve learned about data, BI, cloud platforms, modelling, and analytics didn’t come from carefully staged environments. It came from doing real work on real systems, often under time pressure, sometimes with incomplete information, and occasionally while hoping no one was actively looking at the dashboard I just changed.&lt;/p&gt;

&lt;p&gt;Developing in production isn’t something I aim for. It’s something that happens. Deadlines arrive. Data changes shape. Requirements evolve mid-refresh. The “proper” way gives way to the necessary way, and suddenly production becomes the place where the lesson actually sticks. Not because it’s ideal but because the consequences are real.&lt;/p&gt;

&lt;p&gt;I’m starting this blog to document those lessons as they happen. Not the polished, best-practice version you’d present in hindsight, but the version you learn while fixing something that worked yesterday and inexplicably doesn’t today. The moments where a small change reveals a much bigger misunderstanding. The quiet wins. The loud mistakes. The “oh, that’s how that actually works” realisations.&lt;/p&gt;

&lt;p&gt;This isn’t a guide to doing things perfectly. It’s a record of learning by building, breaking, and fixing. Sometimes carefully. Sometimes directly on production. Always with the goal of understanding things a little better next time.&lt;/p&gt;

&lt;p&gt;If you’ve ever:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;trusted a dashboard and immediately regretted it&lt;/li&gt;
&lt;li&gt;learned more from a production issue than a week of documentation&lt;/li&gt;
&lt;li&gt;made a “small change” that was, in fact, not small&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;then you already understand the premise.&lt;/p&gt;

&lt;p&gt;Future posts will be practical, specific, and occasionally confessional, covering data modelling, BI tools, cloud platforms, and whatever else I happen to be learning the hard way. If nothing else, this will serve as a reminder to my future self of what not to do again.&lt;/p&gt;

</description>
      <category>data</category>
      <category>analytics</category>
      <category>dataengineering</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
