Skip to content

Authoring Measures in CQL

Matt Willer edited this page May 3, 2021 · 28 revisions

This topic will provide a complete discussion of using Clinical Quality Language (CQL) to author electronic Clinical Quality Measures (eCQMs). The discussion assumes familiarity with clinical quality measurement in general, and the representation of eCQMs using Health Quality Measure Format (HQMF) and Quality Data Model (QDM) in particular.

Introduction

Clinical Quality Language (CQL) is a high-level query language, meaning that it can be used to write expressions that determine what data is to be returned, not how it is to be returned. How the data is to be returned is part of the implementation of a quality measure, and may be accomplished in any number of ways (e.g. by queries in a database system, or map-reduce processing on a Hadoop cluster), so CQL is intentionally silent on any of those details, allowing the logic expressed by CQL queries to be used in a broad variety of implementation environments to achieve the same result.

One of the central constructs available in CQL is the expression, which is any sequence of CQL that returns a value. Values are the data that CQL operates on, and can be simple values like the integer 5 and the string John Doe, or they can be complex structures (or rows, also called tuples) like a QDM Encounter, Performed, which contains attributes such as admissionSource and relevantPeriod that are themselves values of different types.

The type of value that an expression returns is based on the contents of the expression. For example, an expression that consists only of a value, such as 5 will simply return that value. Operators, such as + and * can be used to create more complex expressions such as 2 + 3, which will return the result of evaluating the operation, the value 5 in this case. Expressions can also make use of functions, such as CalculateAge() that allow more complex operations to be defined and reused.

One of the most important types of values available in CQL is the list, which is a sequence of values of any type. Lists can contain simple values, like a list of integers, { 1, 2, 3 }, or they can contain tuples, like a list of encounters.

CQL includes a full suite of operators and functions to allow expressions to be written that can describe the logic and criteria used to precisely represent clinical quality measures. The following sections will discuss how to use this language to author a measure from the ground up.

Measure Type

One of the first considerations when expressing a clinical quality measure in CQL is determining what the measure is counting. Is the measure patient-based, meaning that the measure is expressed as some relationship between populations of patients, or is the measure counting some other kind of case such as encounters or procedures?

If the measure is patient-based, then the criteria we define will all return true or false for each specific patient, indicating whether the patient is part of the population being defined by that criteria. A true or false value is type Boolean in CQL, and many of the built-in operations such as comparison (e.g. value > 5) return values of this type.

For example, to express the demographic criteria for a patient-based measure, we can define an In Demographic expression:

define "In Demographic":
  AgeInYearsAt(start of "Measurement Period") >= 13

This example returns true or false for a given patient, depending on whether they satisfy the criteria (13 years of age or older at the start of the measurement period).

Often, we want to identify whether a patient has had a particular type of encounter as part of a patient-based measure. For example:

define "Inpatient Encounters":
  ["Encounter, Performed": "Inpatient Encounter Codes"] Encounter
    where Encounter.relevantPeriod during "Measurement Period"

This expression returns a list of encounters for the patient that match one of the codes in the Inpatient Encounter Codes value set, and that occurred during the measurement period. Now because the result of this expression is a list of encounters, we use an exists to turn the value into a Boolean so we combine it with the previous expression:

define "Initial Population":
  "In Demographic"
    and exists ("Inpatient Encounters")

The exists operator works on a list of values and returns true if the list has any values, and false if the list is empty.

By contrast, if we are counting encounters in an episode-of-care measure, the population criteria will all need to result in lists of encounters:

define "Measurement Period Encounters":
  ["Encounter, Performed": "Ambulatory/ED Visit"] Encounter
    where Encounter.relevantPeriod during "Measurement Period"
      and "In Demographic"

define "Pharyngitis Encounters With Antibiotics":
  "Measurement Period Encounters" Encounter
    with "Pharyngitis" Pharyngitis
      such that Pharyngitis.relevantPeriod includes Encounter.relevantPeriod
        or Pharyngitis.relevantPeriod starts during Encounter.relevantPeriod
    with "Antibiotics" Antibiotics
      such that Antibiotics.authorDatetime 3 days or less after start of Encounter.relevantPeriod

Since the measure is counting encounters, we can return the list in the initial population:

define "Initial Population":
  "Pharyngitis Encounters With Antibiotics"

Accessing Clinical Information

Once we've identified the type of information the measure will be counting, we need to describe what information needs to be retrieved from the source in order to build the population criteria. QDM allows us to describe clinical information as Data Elements, which identify a Data Type and a Value Set. The Data Type combines a Category of clinical information, such as Encounter, Medication, or Procedure, with a context, such as Performed, Administered, or Ordered. The Data Type then determines the structure of the information. For example, the Medication, Administered Data Type has the following attributes:

  • authorDatetime
  • relevantPeriod
  • dosage
  • supply
  • frequency
  • reason
  • negationRationale

In addition to the Data Type, a QDM Data Element specifies a Value Set to indicate precisely what kinds of information should be retrieved. For example, the Outpatient Encounter Codes Value Set identifies encounters that take place in an outpatient setting. The QDM Data Element is then:

"Encounter, Performed: Outpatient Encounter Codes"

In CQL, this is expressed using a retrieve, which encloses the QDM Data Element information in square brackets ([]):

["Encounter, Performed": "Outpatient Encounter Codes"]

This retrieve expression results in only the highlighted encounters, because they have codes that match the "Outpatient Encounter Codes" Value Set:

retrieve-example

This table depicts the instances (or rows) of encounters that might be present in an EHR for a particular patient. It only shows the id, code, and the relevantPeriod attributes for each instance. Note that every QDM Data Element has an implicit id and code that identifies the instance and the specific code for that instance.

Note that the quotation marks in this expression are important, as they are delineating the names of the constructs involved:

Identifier Description
"Encounter, Performed" The name of the QDM Data Type
"Outpatient Encounter Codes" The name of the Value Set

Defining Variables

Any expression in CQL can be given a name so that it can be reused by referencing it in other expressions. The define statement is used for this purpose:

define "Outpatient Encounters":
  ["Encounter, Performed": "Outpatient Encounter Codes"]

With this statement in a CQL library, the identifier "Outpatient Encounters" can now be used as a variable in other expressions.

Queries

Another central construct in CQL is the query, which is a specific type of expression that allows relationships between data to be easily and precisely expressed. Queries in CQL are clause-based meaning that they have different types of clauses that can be used depending on what operations need to be performed on the data. Each clause in a query is introduced with a different keyword, and must appear in a particular location within the query in order to be valid CQL.

The general structure of a CQL query is:

<source> <alias>
  <with or without clauses>
  <where clause>
  <return clause>
  <sort clause>

All the clauses are optional, so the simplest query is just a source and an alias:

"Outpatient Encounters" Encounter

Here, the source is just a reference to Outpatient Encounters, which is an expression that returns a list of encounters. This source is given the alias Encounter. The alias allows the elements of the source to be referenced anywhere within the query. However, this simple query doesn't have any clauses, so it simply returns the same result as the source.

Where Clause

The where keyword introduces a where clause, which allows you to filter the results of the source:

"Outpatient Encounters" Encounter
  where Encounter.relevantPeriod during "Measurement Period"

This query returns only those encounters from the source whose relevantPeriod occurred entirely during the measurement period:

where-example

The where clause allows you to specify any condition in terms of the aliases introduced in the query, Encounter in this case. The condition in the where clause is evaluated for every encounter in the "Outpatient Encounters" query source, and the result then includes only those encounters for which the condition evaluated to true.

Relationships (With and Without Clauses)

Describing relationships between data is so common in quality measurement that CQL provides special constructs to make expressing these relationships as simple as possible, the with and without keywords.

The with keyword can be used to describe cases that should only be considered if a related data item is present:

["Encounter, Performed": "Outpatient"] Encounter
  with ["Laboratory Test, Performed": "Streptococcus Test"] Test
    such that Test.resultDatetime during Encounter.relevantPeriod

with-example

This query limits the outpatient encounters returned to only those that had a streptococcus test performed during the encounter. The such that clause describes the condition of the relationship, which is expressed in terms of the aliases Encounter (for the main source of the query) and Test, which is only available in the such that clause.

In addition, the without keyword can be used to describe cases that should only be considered if a particular data item is not present:

["Encounter, Performed": "Outpatient"] Encounter
  without ["Laboratory Test, Performed": "Streptococcus Test"] Test
    such that Test.resultDatetime during Encounter.relevantPeriod

without-example

This query limits the outpatient encounters returned to only those that did not have a streptococcus test performed during the encounter. Just like the with clause, the without clauses uses such that to describe the condition of the relationship.

Where versus Such That

Note that the where and such that phrases serve very similar purposes in that they are both used to describe a condition that must hold. The difference is that such that is only valid as part of a with or without clause, and is the only place where the aliases introduced in those clauses can be referenced. For example, consider the following query:

["Encounter, Performed": "Outpatient"] Encounter
  without ["Laboratory Test, Performed": "Streptococcus Test"] Test
    such that Test.resultDatetime during Encounter.relevantPeriod
  where Test.result is not null

This query is invalid because the alias Test is only available within the without clause that introduced it.

Combining With/Without Clauses

The with and without clauses apply to the whole query; every additional with or without clause further restricts the cases that will be returned. This means that these clauses can't be used directly to express presence of one item or another. Combining with and without clauses optionally can be accomplished using a union operator:

"Encounters With Comfort Measures"
  union "Encounters With Expected Discharge Status"

Ordering Results with Sort

The order in which the items are returned from a query can be described using a sort clause:

["Encounter, Performed": "Outpatient"] Encounter
  sort by start of relevantPeriod

This query returns the results sorted by the start of the relevantPeriod element, ascending:

sort-example

Note that the alias Encounter is not used in the sort clause. This is because the sort applies to the output of the query, so only elements that are present in the result can be referenced and there is no need to identify the source alias.

Also, any element of the result can be the target of the sort, but the values of the element must be comparable (i.e. must be able to be compared using <, > and =). Consider the following invalid query:

["Encounter, Performed": "Outpatient"] Encounter
  sort by relevantPeriod

This query is invalid because relevantPeriod is an interval, and intervals cannot be unambiguously compared with >.

Values and Calculation

Strings

Strings in CQL are sequences of characters that appear within single-quotes (').

'John Doe'
'John O\'Mally'
'John Doe' = 'john doe' // false
'Deer' < 'Doe' // true

If a string contains a single-quote, the back-slash character (\) is used to escape the string. Standard escape characters can also be used to include special characters like tabs, carriage returns, and line feeds.

CQL supports string comparison for all the comparison operators (=, !=, <=, >=, <, and >).

String comparison is case-sensitive, and based on the Unicode value of each character.

Numbers

CQL supports Integers (whole numbers) and Decimals.

5
5.0

In calculations and comparisons, integers are implicitly converted to decimals when necessary.

5 + 5.0   // returns 10.0

Comparison of decimals ignores precision.

5.0 = 5.00   // returns true

CQL uses standard mathematical operator precedence.

2 + 5 * 10   // returns 52

Parentheses can be used to force precedence.

(2 + 5) * 10   // returns 70

Division in CQL always returns a decimal, use div to perform integer division.

10 / 2     // returns 5.0
10 div 2   // returns 5

The mod operator returns the remainder of an integer division.

10 mod 2   // returns 0

Rounding and Exponents

CQL supports standard rounding, 0.5 and above rounds up, 0.4 and below rounds down. The second argument to the Round() operator, if supplied, specifies the desired precision of the result.

Round(5.5)       // returns 6.0
Round(5.55, 1)   // returns 5.6

Truncate() returns the integer component of a decimal.

Truncate(5.5)    // returns 5
Truncate(-5.5)   // returns -5

Floor() returns the greatest integer less than a decimal.

Floor(5.5)    // returns 5
Floor(-5.5)   // returns -6

Ceiling() returns the least integer greater than a decimal.

Ceiling(5.5)     // returns 6
Ceiling(-5.5)    // returns -5

CQL supports exponents and roots with ^.

5 ^ 2      // returns 25
25 ^ 0.5   // returns 5.0

Logarithms to a given base use Log().

Log(25, 5)    // returns 2.0
Log(5, 25)    // returns 0.5

Natural logarithms use Ln() and Exp().

Ln(10)                  // returns 2.30258209288405
Exp(2.30258209288405)   // returns 10.0

Quantities

Quantities in CQL are a number followed by a UCUM unit, enclosed in single-quotes:

25 'mg'
100 'cm2'

CQL supports arithmetic and comparison operators for quantities. Implementations are required to respect units, but not necessarily conversions between units. Arithmetic operators in particular must return quantities with appropriate units, but not necessarily converted.

1 'm' = 100 'cm'    // returns true
10 'cm' * 10 'cm'   // returns 100 'cm2'

NOTE: An implementation may raise an error for an unsupported unit conversion operation.

Dates and Times

CQL supports DateTime, a point-in-time on the Western calendar, specified with integers for year, month, day, hour, minute, second, and millisecond, plus a timezone.

@2014-01-25
@2014-01-25T14:30:14.5

CQL also supports Time, a point-in-time in a 24-hour period, specified with integers for hour, minute, second, and millisecond, plus a timezone.

@T12:00:00.0Z
@T14:30:14.5-07:00

Both DateTime and Time support partial values, but only for trailing precisions (i.e., if you specify a day, you must also specify a year and month).

@2014
@2014-01
@T14
@T14:30

If not supplied, timezone is assumed based on the evaluation context.

CQL also supports construction of DateTime and Time values as expressions. This is useful for building DateTime and Time values from the results of other calculations or elements of data items.

DateTime(2014, 7, 5)
Time(14, 30)

You can use date from to extract the date (with no time components) from a DateTime value.

date from @2014-01-25T14:30:14    // returns 2014-01-25

You can use time from to extract the time from a DateTime value:

time from @2014-01-25T14:30:14    // returns T14:30:14

You can use the name of a component to extract it from a DateTime or Time value:

year from @2014-01-25             // returns 2014

Now(), Today() and TimeOfDay() return the DateTime, Date, and Time, respectively, of the evaluation context.

Date and Time Comparisons

DateTime and Time values can be compared using the standard comparison operators (=, !=, <=, >=, <, >).

@2014-01-15 = @2014-02-15          // returns false
@2014-01-15 < @2014-02-15          // returns true
@2014-01-15 <= @2014-02-15         // returns true

Precision-based comparison can also be performed using same as, before/after of and same or before/after

@2014-01-15 same year as @2014-02-15         // returns true
@2012-01-15 same year or before @2014-02-15  // returns true
@2012-01-15 before year of @2014-02-15       // returns true

Date and Time Arithmetic

CQL supports time-valued quantities with the name (singular or plural) of the precision as the unit:

1 day
2 years
30 minutes

The UCUM units can also be used (with single-quotes):

1 'd'
2 'a'
30 'min'

Time-valued quantities (or durations) can then be added to or subtracted from DateTime and Time values, with the expected semantics for durations with variable days such as years and months:

Today() - 1 year
@2014-02-01T14:30 + 30 minutes      // 2014-02-01T15:00
@2014 + 24 months                   // 2016

Time Interval Calculations

To determine the length of time between two dates, CQL provides two different approaches, duration the number of whole periods between two dates, and difference, the number of period boundaries crossed between two dates.

The first approach, calculating the duration, determines the number of whole periods that occur between the two dates. Conceptually, the calculation is performed by considering the two dates on a timeline, and counting the number of whole periods that fit on that timeline between the two dates. For example:

Date 1: 2012-03-10
Date 2: 2013-03-10
Duration In Years: years between Date1 and Date2

The Duration In Years expression gives one year, because an entire year has passed between the two dates. Note that time is considered for the purposes of calculating the number of years:

DateTime 1: 2012-03-10 10:20:00
DateTime 2: 2013-03-10 09:20:00
Duration in Years: years between DateTime1 and DateTime2

This expression gives zero years, because the year has not passed until 10:20:00 on the day in the following year. To calculate the number of years, ignoring the time, extract the date from the date/time value:

DateTime 1: 2012-03-10 10:20:00
DateTime 2: 2013-03-10 09:20:00
Duration In Years: years between (date from DateTime1) and (date from DateTime2)

The second approach, calculating the difference, determines the number of boundaries crossed between two dates. To illustrate the difference, consider the following example:

Date 1: 2012-12-31
Date 2: 2013-01-01
Duration In Years: years between Date1 and Date2
Difference In Years: difference in years between Date1 and Date2

The Duration In Years expression returns zero because a full year has not passed between the two dates. However, the Difference In Years expression returns 1 because one year boundary was crossed between the two dates.

Calculating Duration in Years

Definition

In CQL, a year is defined as the duration of any time interval which starts at a certain time of day at a certain calendar date of the calendar year and ends at:

  • The same time of day on the same calendar date of the next calendar year, if it exists
  • The same time of day on the immediately following calendar date of the next calendar year, if the same calendar date of the next calendar year does not exist.

Note: When in the next calendar year the same calendar date does not exist, the ISO states that the ending calendar day has to be agreed upon. The above convention is used in CQL as a resolution to this issue.

Examples

  1. Month (date 2) < month (date 1): Duration (years) = year (date 2) - year (date 1) - 1

Example 1:
Date 1: 2012-03-10 22:05:09
Date 2: 2013-02-18 19:10:03
Duration = year (date 2) - year (date 1) - 1 = 2013 - 2012 - 1 = 0 years

  1. Month (date 2) = month (date 1) and day (date 2) >= day (date 1)
    Duration (years) = year (date 2) - year (date 1)

Example 2.a: day (date 1) = day (date 2)
Date 1: 2012-03-10 22:05:09
Date 2: 2013-03-10 22:05:09
Duration = year (date 2) - year (date 1) = 2013 - 2012 = 1 year

Note: Time of day is important in this calculation. If the time of day of Date 2 were less than the time of day for Date 1, the duration of the time interval would be 0 years according to the definition.

Example 2.b: day (date 2) > day (date 1)
Date 1: 2012-03-10 22:05:09
Date 2: 2013-03-20 04:01:30
Duration = year (date 2) - year (date 1) = 2013 - 2012 = 1 year

  1. Month (date 2) = month (date 1) and day (date 2) < day (date 1)
    Duration (years) = year (date 2) - year (date 1) - 1

Example 3.a:
Date 1: 2012-02-29
Date 2: 2014-02-28
Duration = year (date 2) - year (date 1) - 1 = 2014 - 2012 - 1 = 1 year

  1. Month (date 2) > month (date 1)
    Duration (years) = year (date 2) - year (date 1)

Example 4.a:
Date 1: 2012-03-10 11:16:02
Date 2: 2013-08-15 21:34:16
Duration = year (date 2) - year (date 1) = 2013 - 2012 - 1 year

Example 4.b:
Date 1: 2012-02-29 10:18:56
Date 2: 2014-03-01 19:02:34
Duration = year (date 2) - year (date 1) = 2014 - 2012 = 2 years

Note: Because there is no February 29 in 2014, the number of years can only change when the date reaches March 1, the first date in 2014 that surpasses the month and day of date 1 (Feburary 29).

Calculating Duration in Months

Definition

A month in CQL is defines as the duration of any time interval which starts at a certain time of day at a certain calendar day of the calendar month and ends at:

  • The same time of day at the same calendar day of the ending calendar month, if it exists
  • The same time of day at the immediately following calendar date of the ending calendar month, if the same calendar date of the ending month in the ending year does not exist.

Notes: When in the next calendar year the same calendar date does not exist, the ISO states that the ending calendar day has to be agreed upon. The above convention is used in CQL as a resolution to this issue.

Examples

  1. Day (date 2) >= day (date 1)
    Duration (months) = (year (date 2) - year (date 1)) * 12 + (month (date 2) - month (date 1))

Example 1.a:
Date 1: 2012-03-01 14:05:45
Date 2: 2012-03-31 23:01:49
Duration = (year (date 2) - year (date 1)) * 12 + (month (date 2) - (month (date 1))
= (2012 - 2012) * 12 + (3 - 3) = 0 months

Example 1.b:
Date 1: 2012-03-10 22:05:09
Date 2: 2013-06-30 13:00:23
Duration = (year (date 2) - year (date 1)) * 12 + (month (date 2) - (month date 1))
= (2013 - 2012) * 12 + (6 - 3) = 12 + 3 = 15 months

  1. Day (day 2) < day (date 1)
    Duration (months) = (year (date 2) - year (date 1)) * 12 + (month (date 2) - month (date 1)) - 1

Example 2:
Date 1: 2012-03-10 22:05:09
Date 2: 2013-01-09 07:19:33
Duration = (year (date 2) - year (date 1)) * 12 + (month (date 2) - month (date 1)) - 1
= (2013 - 2012) * 12 + (1 - 3) - 1 = 12 - 2 - 1 = 9 months

Calculating Duration in Weeks

Definition

In CQL, a week is defined as a duration of any time interval which starts at a certain time of day at a certain calendar day at a certain calendar week and ends at the same time of day at the same calendar day of the ending calendar week. In other words, a complete week is always seven days long.

Examples

  1. Duration = [date 2 - date 1 (days)] / 7

Example 1:
Date 1: 2012-03-10 22:05:09
Date 2: 2012-03-20 07:19:33
Duration = [# days (month (date 1)) - day (date 1) + # days (month (date 1) + 1) + #days (month (date 1) + 2) + ... + # days (month (date 2) - 1) + day (date 2)] / 7
= (20 - 10) / 7 = 10 / 7 = 1 week

Calculating Duration in Days

Definition

In CQL, a day is defined as a duration of any time interval which starts at a certain calendar day and ends at the next calendar day (1 second to 23 hours, 59 minutes, and 59 seconds).

The duration in days between two dates will generally be given by subtracting the start calendar date from the end calendar date, respecting the time of day between the two dates.

Examples

  1. Time (date 2) < time (date 1)
    Duration = [date 2 - date 1 (days)] - 1

Example 1:
Date 1: 2012-01-31 12:30:00
Date 2: 2012-02-01 09:00:00
Duration = 02-01 - 01-31 - 1 = 0 days

  1. Time (date 2) >= time (date 1)
    Duration = date 2 - date 1 (days)

Example 2:
Date 1: 2012-01-31 12:30:00
Date 2: 2012-02-01 14:00:00
Duration = 02-01 - 01-31 = 1 day

Calculating Duration in Hours

Definition

In CQL, an hour is defined as 60 minutes. The duration in hours between two dates is the number of minutes between the two dates, divided by 60. The result is truncated to the unit.

Examples

  1. Example 1:
    Date 1: 2012-03-01 03:10:00
    Date 2: 2012-03-01 05:09:00
    Duration = 1 hour

  2. Example 2:
    Date 1: 2012-02-29 23:10:00
    Date 2: 2012-03-01 00:10:00
    Duration = 1 hour

  3. Example 3:
    Date 1: 2012-03-01 03:10
    Date 2: 2012-03-01 04:00
    Duration = 0 hours

Calculating Duration in Minutes

Definition

In CQL, a minute is defined as 60 seconds. The duration in minutes between two dates is the number of seconds between the two dates, divided by 60. The result is truncated to the unit.

Examples

  1. Example 1:
    Date 1: 2012-03-01 03:10:00
    Date 2: 2012-03-01 05:20:00
    Duration = 130 minutes

  2. Example 2:
    Date 1: 2012-02-29 23:10:00
    Date 2: 2012-03-01 00:20:00
    Duration = 70 minutes

Difference Calculations

Difference calculations are performed by truncating the date/time values at the next precision, and then performing the corresponding duration calculation on the truncated values.

Examples

  1. Example 1:
    Date 1: 2012-03-01 03:10:00
    Date 2: 2012-12-31 10:10:00
    Difference (years) = Duration (years) between 2012-01-01 00:00:00 and 2012-01-01 00:00:00
    Difference (years) = 0

  2. Example 2:
    Date 1: 2012-12-31 03:10:00
    Date 2: 2013-01-01 10:10:00
    Difference (years) = Duration (years) between 2012-01-01 00:00:00 and 2013-01-01 00:00:00
    Difference (years) = 1

  3. Example 3:
    Date 1: 2016-10-10 09:00:00
    Date 2: 2016-10-11 11:59:00
    Difference (days) = Duration (days) between 2016-10-10 00:00:00 and 2016-10-11 00:00:00
    Difference (days) = 1

  4. Example 4:
    Date 1: 2016-10-10 09:00:00
    Date 2: 2016-10-12 00:00:00
    Difference (days) = Duration (days) between 2016-10-10 00:00:00 and 2016-10-12 00:00:00
    Difference (days) = 2

Intervals

CQL supports intervals of numbers and date and time values.

Interval[3, 5)            // 3, 4
Interval[3.0, 5.0)        // >= 3.0, < 5.0
Interval[@2014-01-01T00:00:00.0, @2015-01-01T00:00:00.0)

Intervals use standard mathematical notation to indicate open and closed (i.e. whether the endpoint is included in (closed) or excluded from (open) the interval).

You can test for membership with contains and in, and you can determine the boundaries of an interval using start of and end of:

Interval[3, 5) contains 4         // returns true
4 in Interval[3, 5)               // returns true

start of Interval[3, 5)           // returns 3
end of Interval[3, 5)             // returns 4

You can determine the width of an interval using width of

width of Interval[3, 5)          // returns 2

Comparing Intervals

CQL provides a complete set of interval comparison operators:

comparing-intervals

Timing Phrases

CQL also supports timing phrases that make it easier to express precise relationships between intervals using natural language. The before and after operators can have a prefix of starts or ends, and a suffix of start or end. For example:

IntervalX starts before start IntervalY

timing-phrases-starts

The before and after operators can also take an offset that indicates how far away a given relationship should be. The offset can be absolute, indicating that the boundary of the interval must be on the offset, or it can be relative, indicating that the boundary must be at least on the offset:

IntervalX starts 3 days before start IntervalY
IntervalX starts 3 days or more before start IntervalY

timing-phrases-starts-with-offset

You can also specify a range for the boundary relationship using the within..of operator:

IntervalX starts within 3 days of start IntervalY

timing-phrases-starts-within

Lists

CQL supports lists of values of any type:

{ 'a', 'b', 'c' }
{ 1, 2, 3 }

More importantly, the results of queries and retrieves are represented as lists in CQL.

Picking Items in a List

Because the results of queries and retrieves are lists, you can pick items based on order using First() and Last():

First("Sorted Encounters")

first-example

Last("Sorted Encounters")

last-example

Because "Sorted Encounters" is ordered by the start of the relevantPeriod, First() returns the oldest encounter, while Last() returns the most recent.

You can also use the indexer ([ ]) to pick out any item by its index in the list.

Indexes in CQL are 0-based, so the first item is index 0, the second item is index 1, and so on.

"Sorted Encounters"[0]

index-example

"Sorted Encounters"[1]

index-example-1

Note that whenever you're performing operations that rely on the order of elements in the list, be sure to use a sort clause to get the appropriate ordering. Otherwise, the result may be different than you expect.

List Comparison and Membership

You can test for membership of items in a list using the contains and in operators. You can also compare lists using equality (=), and the includes and included in operators:

X contains 3                // returns true
3 in X                      // returns true
X includes Y                // returns true
Y included in X             // returns true

list-comparison

List Operations

To combine two lists, eliminating duplicates, use the union operator:

list-union

The intersect operator results in a list containing only the elements that appear in both lists:

list-intersect

The except operator results in a list containing only the elements of the first list that are not present in the second list:

list-except

Wiki Index

Home

Authoring Patterns - QICore v4.1.1

Authoring Patterns - QICore v5.0.0

Authoring Patterns - QICore v6.0.0

Authoring Measures in CQL

Composite Measure Development

Cooking with CQL Examples

Cooking with CQL Q&A All Categories
Additional Q&A Examples

CQL 1.3 Impact Guidance

CQL Error Messages

Developers Introduction to CQL

Discussion Items

Example Measures

Formatting and Usage Topics

Formatting Conventions

Library Versioning

Negation in QDM

QDM Known Issues

Specific Occurrences

Specifying Population Criteria

Supplemental Data Elements

Terminology in CQL

Translator Options For Measure Development

Unions in CQL

Clone this wiki locally