Clinical Quality Language Technical Example

Last updated: May 18, 2026
Roles: Informaticist Software Engineer

This module is meant for technical users who are interested in understanding the details of authoring clinical logic using HL7 Clinical Quality Language (CQL). Please read or review the Clinical Quality Language introduction before continuing.

1 Example Overview: Elevated A1C

In this module, we will walk through the steps to create a CQL library that contains expressions representing the following criteria:

Adult patients who had an outpatient encounter during the study period and who have elevated A1C.

We will use the four CDS knowledge levels as a framework for working through this example [1]. Although our example is not necessarily a complete clinical decision support artifact, we can use the same process to move from a narrative representation to an executable representation.

2 Knowledge Level 1: Narrative

Knowledge level 1 is the narrative representation. The narrative representation may come from a clinical guideline, a research paper, a best practice, or any other human-readable clinical statement. In our example, the level 1 narrative representation is:

Adult patients who had an outpatient encounter during the study period and who have elevated A1C.

3 Knowledge Level 2: Semi-Structured

Knowledge level 2 is the semi-structured representation. This may often be represented using a decision tree, flowchart, or high-level pseudocode. One benefit of specifying a level 2 representation before moving on to the level 3 representation is that it often surfaces hidden assumptions and/or ambiguous phrasing in the narrative. Although our narrative is relatively straightforward, there are still some phrases that would benefit from greater specificity:

  • Who qualifies as an adult? Is it anyone 18 years old and above, or is it intended to target a different age range? Is there an age cut-off – for example, 75 years of age?
  • What qualifies as an outpatient encounter? Do telehealth encounters count? What about outpatient surgeries?
  • How do we determine if a patient has elevated A1C? What is considered “elevated”? Should we consider all A1C results or only the most recent?

When we restate the narrative in a semi-structured format, we often have to resolve these ambiguities. In our example, we will resolve them in the following way:

  • We will indicate that anyone 18 years or older qualifies as an adult.
  • We will use a value set to provide a list of encounter codes that represent all qualifying outpatient encounter types.
  • We will use the most recent HbA1c lab result to measure A1C and consider it “elevated” if it is >= 6.5%.

Using the original narrative and the decisions above, we might come up with the following level 2 semi-structured representation that uses a bullet list and boolean logic:

Note

The object identifiers (OIDs) for outpatient encounter (2.16.840.1.113762.1.4.1160.24) and HbA1c result (2.16.840.1.113883.3.464.1003.198.12.1013) above link to value sets in the Value Set Authority Center (VSAC). Viewing the contents of these value sets requires a free account.

4 Knowledge Level 3: Structured

Knowledge level 3 is the structured representation. Level 3 representations are computable and shareable but may still require local adaptation before they can be used in any specific environment. In our case, we will use CQL and ELM for the level 3 representation, but other structured languages and formats are also valid (e.g., BPM+).

We will work from the level 2 representation above to develop the level 3 structured representation using CQL.

4.1 Library and Data Model Declarations

Every CQL library should have a name and version. These are typically declared at the top of the file using library and version keywords. We will call our library ExampleElevatedHbA1c and give it version 0.0.1 to indicate it is in the early stages of development.

library ExampleElevatedHbA1c version '0.0.1'

Next, we must indicate what data model we’re using and include any helper libraries for that data model. CQL uses the using keyword to declare models and the include keyword to include additional libraries in the logic. In our example, we are using the FHIR data model, which uses the FHIRHelpers library for common FHIR-related CQL functions and conversions.

using FHIR version '4.0.1'
include FHIRHelpers version '4.0.1'

4.2 Terminology: Outpatient Encounters and HbA1c Lab Tests

CQL requires authors to declare terminology requirements up front and provides valueset, codesystem, concept, and code keywords for doing so. In our case, the level 2 representation indicates specific value sets from VSAC that should be used when querying for outpatient encounters and HbA1c labs.

valueset "Outpatient Clinical Encounters": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1160.24'
valueset "HbA1c Laboratory Test": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1013'

You may have noticed that the above value set definitions use URLs to identify the value sets rather than using the OIDs directly. This is because the Using CQL with FHIR implementation guide indicates that value set identifiers must use canonical URLs as value set identifiers. The FHIR terminology service for VSAC resources indicates the following form for canonical value set URLs: http://cts.nlm.nih.gov/fhir/ValueSet/{OID}.

4.3 Parameters: Study Period

CQL parameters allow authors to define data elements that can be specified at evaluation time. In our example, we don’t want to tie the study period to a specific set of dates, so we will use the parameter keyword to define the study period as a DateTime interval that is passed in during execution. This means that the CQL can be executed with any arbitrary study period rather than encoding a specific study period into the CQL logic.

parameter "Study Period" Interval<DateTime>

4.4 Context: Patient

Next, we will use the context keyword to declare that the following CQL expressions are intended to be processed in the context of a patient.

context Patient

CQL only requires that data models support the Unfiltered context, but most data models support additional contexts as well (especially the Patient context). The FHIR R4 data model currently supports the Device, Patient, Practitioner, and Unfiltered contexts.

4.5 Expression: Age >= 18 years

We’re finally at the point where we can define the first patient-based criterion: Is Adult. As indicated by the level 2 representation, we will define “adult” as patients aged 18 years and above. To represent this in CQL, we’ll use CQL’s built-in AgeInYears() function.

define "Is Adult": AgeInYears() >= 18

Note that we use a define keyword to give the CQL expression a name that we can reference later. Statement names should reflect what they represent, so we’ve called it Is Adult since it returns a true or false value depending on whether the patient is an adult.

You may have also noticed that we use the familiar >= syntax to express the concept of “greater than or equal to.” CQL supports a variety of comparison operators like this as well as other mathematical operators like +, -, /, and more.

4.6 Expression: Outpatient Encounter DURING study period

To determine if the patient had an outpatient encounter in the study period, we must query for Encounters that have a code in the Outpatient Clinical Encounters value set and whose encounter period occurred during the Study Period.

define "Has Outpatient Encounter During Study Period":
  exists [Encounter: "Outpatient Clinical Encounters"] E
    where E.period during "Study Period"

In the CQL query above, [Encounter: "Outpatient Clinical Encounters"] retrieves Encounter resources from the FHIR server and filters them by the Outpatient Clinical Encounters value set. The letter E is used as an alias so we can easily refer to each returned Encounter in the where clause. The during keyword is one of many interval operators that allows us to compare specific dates with date ranges and/or compare date ranges with other date ranges. CQL also defines an additional set of date and time operators for performing other comparisons and calculations using dates and times.

Note that we also prefixed the entire query with the exists keyword. This provides a simple way to check if the query returns at least one result. In our case, it will return true if at least one matching encounter exists and return false otherwise.

4.7 Expression: MOST RECENT HbA1c result >= 6.5%

Next, we want to check the most recent HbA1c result value. First, we’ll find the most recent HbA1c laboratory test by querying for Observations with a code in the HbA1c Laboratory Test value set. Since we want the most recent one, we will sort the results by their issued dates and return the last one using CQL’s built-in Last function. The Last function comes from CQL’s set of list operators.

define "Most Recent HbA1c": 
  Last([Observation: "HbA1c Laboratory Test"] HbA1c sort by issued)

To see if the result is 6.5% or above, we can reference the Most Recent HbA1c expression we just defined and check its value. When working with quantity units in clinical logic, it is a best practice to use the Unified Code for Units of Measure (UCUM). CQL indicates that UCUM units must be wrapped in single quotes, so when we do the comparison, we wrap the UCUM unit for percent (%) in single quotes: '%'.

define "Has Elevated HbA1c": "Most Recent HbA1c".value >= 6.5 '%'

4.8 Combining the Expressions

Finally, we can combine our statements using the and keyword to provide one expression that checks all of the criteria.

define "Is Adult with Outpatient Encounter and Elevated HbA1c":
  "Is Adult" and
  "Has Outpatient Encounter During Study Period" and
  "Has Elevated HbA1c"

CQL can also support more complex combinations via additional logical operators such as or, xor, and not.

4.9 The Complete ExampleElevatedHbA1c CQL Library

Putting all of the steps above together, we end up with the following library. Note that this library is for example purposes only. It has not been fully tested and should not be used in production environments.

library ExampleElevatedHbA1c version '0.0.1'
using FHIR version '4.0.1'
include FHIRHelpers version '4.0.1'

valueset "Outpatient Clinical Encounters": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1160.24'
valueset "HbA1c Laboratory Test": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1013'

parameter "Study Period" Interval<DateTime>

context Patient

define "Is Adult": AgeInYears() >= 18

define "Has Outpatient Encounter During Study Period":
  exists [Encounter: "Outpatient Clinical Encounters"] E
    where E.period during "Study Period"

define "Most Recent HbA1c": 
  Last([Observation: "HbA1c Laboratory Test"] HbA1c sort by issued)

define "Has Elevated HbA1c": "Most Recent HbA1c".value >= 6.5 '%'

define "Is Adult with Outpatient Encounter and Elevated HbA1c":
  "Is Adult" and
  "Has Outpatient Encounter During Study Period" and
  "Has Elevated HbA1c"

4.10 The Expression Logical Model (ELM) Representation

The library syntax above is optimized for human authoring and reviewing. Although computers can parse it as-is, most CQL engines prefer to parse the logic in a more machine-friendly format called Expression Logical Model (ELM). ELM is typically exchanged in a JSON or XML format.

As an example of how CQL relates to ELM, consider the following statement from our CQL library:

define "Has Outpatient Encounter During Study Period":
  exists [Encounter: "Outpatient Clinical Encounters"] E
    where E.period during "Study Period"

The following ELM XML representation is equivalent to the CQL above.

<def name="Has Outpatient Encounter During Study Period"
     context="Patient" accessLevel="Public">
  <expression xsi:type="Exists">
    <operand xsi:type="Query">
      <source alias="E">
        <expression xsi:type="Retrieve" dataType="fhir:Encounter"
                    templateId="http://hl7.org/fhir/StructureDefinition/Encounter"
                    codeProperty="type" codeComparator="in">
          <codes xsi:type="ValueSetRef" name="Outpatient Clinical Encounters" preserve="true"/>
        </expression>
      </source>
      <where xsi:type="IncludedIn">
        <operand xsi:type="FunctionRef" name="ToInterval" libraryName="FHIRHelpers">
          <signature xsi:type="NamedTypeSpecifier" name="fhir:Period"/>
          <operand xsi:type="Property" path="period" scope="E"/></operand>
        <operand xsi:type="ParameterRef" name="Study Period"/>
      </where>
    </operand>
  </expression>
</def>

4.11 Converting the Full CQL Library to ELM

The CQL-to-ELM translator reference implementation is capable of converting CQL to ELM JSON or ELM XML. This translator is typically invoked programmatically, but you can also invoke it using the online CQL Playground. Try out the following to see how CQL-to-ELM translation works and to get a feel for the ELM representation:

  1. Use your browser to open the CQL Playground site.
  2. Make sure that ELM is selected in the top-level banner.
  3. Copy the ExampleElevatedHbA1c CQL library above and paste it into the text box on the left of the CQL Playground (overwriting the default text).
  4. In the rightmost column, find the Output content type section and check the box next to Pretty print JSON output.
  5. In the rightmost column, find the Compiler options section and uncheck the box next to Enable locators to get a cleaner view.
  6. Investigate the ELM JSON representation in the middle column of the page. Notice the library identifier, usings (data model), includes (helper libraries), parameters, valueSets, contexts, and statements.
  7. In the rightmost column, find the Output content type section and select XML to see the ELM XML representation. There is no option to pretty-print XML directly, but you can copy the XML into an online XML formatter to view it more easily.

Feel free to experiment with the CQL Playground further by editing the CQL to see how it affects the ELM and/or changing the compiler options.

5 Knowledge Level 4: Executable

Knowledge level 4 is the executable representation, suitable for use at a specific site or within a specific system. It builds on level 3 structured artifacts by adding site-specific configuration, local data mappings, and integration into local clinical workflows. In our example, this might include configuring the CQL engine to interface with a local FHIR server, mapping local codes to the standardized codes in our value sets, and exposing the functionality to end users in an appropriate way. Level 4 integrations may leverage other health standards like SMART on FHIR or CDS Hooks, or they may integrate directly using custom APIs and interfaces. This knowledge level represents the final step: the point at which the intent of the narrative has been realized in a specific environment.

6 CQL Resources

The following resources may be useful for researchers who want to dig deeper into some of the topics discussed in this example walkthrough:

References

[1]
A. A. Boxwala et al., “A multi-layered framework for disseminating knowledge for computer-based decision support,” Journal of the American Medical Informatics Association, vol. 18, no. Supplement_1, pp. i132–i139, Dec. 2011, doi: 10.1136/amiajnl-2011-000334.