Reading the workbench’s CQL
Follow one NHI reimbursement clause through the CQL the workbench exports: what each part does, and how the result comes out when data is missing. No programming experience needed.
What CQL is: Clinical Quality Language, the HL7 standard for writing clinical rules. The workbench uses only a small part of it, and this page covers only that part.
The example clause
Every example on this page uses this clause. The workbench splits it into four atomic conditions and two groups:
本品限內科專科醫師處方,病人需為成人(年滿十八歲),且 eGFR ≥ 30 mL/min/1.73m²;未達標準者,須經腎臟科會診同意後使用。Translation: Must be prescribed by an internal medicine specialist. The patient must be an adult (18 or older) with eGFR ≥ 30 mL/min/1.73m². Below that threshold, use requires nephrology consultation approval.
| ID | Meaning |
|---|---|
| C1 | Prescriber is an internal medicine specialist |
| C2 | Patient is 18 or older |
| C3 | eGFR ≥ 30 mL/min/1.73m² |
| C4 | Nephrology consultation approved |
| G2 | C3 or C4 (any one) |
| G1 | C1, C2 and G2 (all; the verdict for the whole clause) |
The full file
This is the complete file renderCql generates for the clause. The next four sections walk through it from top to bottom.
library Clause_Sample version '1.0.0'
using FHIR version '4.0.1'
/* Evidence adapters supply true, false, or null for each reviewed criterion. */
parameter "C1" Boolean default null
parameter "C2" Boolean default null
parameter "C3" Boolean default null
parameter "C4" Boolean default null
context Patient
define "Criterion C1":
"C1"
define "Criterion C2":
"C2"
define "Criterion C3":
"C3"
define "Criterion C4":
"C4"
define "Group G1":
"Criterion C1"
and "Criterion C2"
and "Group G2"
define "Group G2":
"Criterion C3"
or "Criterion C4"
define "Eligibility":
"Group G1"
define "Eligibility Status":
if "Eligibility" is true then 'eligible'
else if "Eligibility" is false then 'ineligible'
else 'insufficient-data'Header: library, using, context
Lines 1, 3 and 11 say what the file is, which data model it uses, and who it is evaluated for.
library Clause_Sample version '1.0.0'- The name and version of this logic, used to tell exported files apart.
using FHIR version '4.0.1'- Declares the FHIR R4 data model. The workbench’s logic doesn’t actually read FHIR data; the next section explains why.
context Patient- Every define after this line is evaluated for one patient.
Parameters: one input per condition
Each atomic condition becomes a parameter of type Boolean. The CQL never queries the record itself: an evidence adapter checks the record first and passes the answer in as a parameter.
parameter "C3" Boolean default nullA parameter only ever holds one of three values. Take C3 (eGFR ≥ 30):
default null means that when nobody supplies a value, the answer is “unknown”, not “doesn’t meet”.
define: naming an expression
define is followed by a name and a colon; its body starts on the next line. The workbench generates three layers:
define "Criterion C3":
"C3"
define "Group G2":
"Criterion C3"
or "Criterion C4"
define "Eligibility":
"Group G1"Criterion C3takes the value of parameter C3 as is.Group G2is G2 on the review screen: C3 or C4.Eligibilitypoints at the outermost group, G1: the verdict for the whole clause.
Note: double quotes hold names, like "Group G2"; single quotes hold text, like 'eligible'.
Three-valued logic: what null does
A parameter can be null, so and, or and not all have to handle “unknown”. The rule fits in one sentence: if the answer is already settled, null doesn’t matter; if it isn’t, the result is null.
| and | true | false | null |
|---|---|---|---|
| true | true | false | null |
| false | false | false | false |
| null | null | false | null |
| or | true | false | null |
|---|---|---|---|
| true | true | true | true |
| false | true | false | null |
| null | true | null | null |
| not | Result |
|---|---|
| true | false |
| false | true |
| null | null |
For example, false and null is false: once one condition fails, “all of them” can’t hold, whatever the other value is. true or null is true for the same reason.
The result: why is true
The last define turns the three-valued result into three statuses:
define "Eligibility Status":
if "Eligibility" is true then 'eligible'
else if "Eligibility" is false then 'ineligible'
else 'insufficient-data'is true holds only when the value is true. On null it returns false, not null, so the three branches map exactly to the three outcomes: eligible, ineligible and insufficient-data.
Don’t write it this way: when the condition of a CQL if is null, it takes the else branch, so a patient with missing data would be marked ineligible.
define "Eligibility Status":
if "Eligibility" then 'eligible'
else 'ineligible'Four scenarios, step by step
Plug in each set of parameters and work from the inside out: G2 first, then G1.
| Scenario | C1 | C2 | C3 | C4 | G2 | G1 | Result |
|---|---|---|---|---|---|---|---|
| eGFR meets threshold | true | true | true | false | true | true | eligible |
| Under 18 | true | false | true | true | true | false | ineligible |
| No eGFR, consult approved | true | true | null | true | true | true | eligible |
| No eGFR, no consult record | true | true | null | null | null | null | insufficient-data |
In row three C3 is null, but C4 is true, and null or true is already settled as true, so the patient is still eligible. In row four both C3 and C4 are null, so G2 can’t be decided and the whole clause is insufficient-data until an eGFR result or a consult record comes in.
Try it yourself
Set each condition to meets, doesn’t meet or not found, and watch G2, G1 and the result change. Start from a scenario in the table above, then change one cell.
- C1Prescriber is an internal medicine specialist
- C2Patient is 18 or older
- C3eGFR ≥ 30 mL/min/1.73m²
- C4Nephrology consultation approved
G2=C3orC4=nullortrue→true
One side is true, which settles the or as true; the null on the other side doesn’t matter.
G1=C1andC2andG2=trueandtrueandtrue→true
All three are true.
Eligibility Status →'eligible'Eligible
Eligibility is true, so the first branch applies.
FAQ
What is CQL?
CQL (Clinical Quality Language) is an HL7 standard for writing clinical rules, such as reimbursement criteria, quality measures and clinical decision support, in a syntax close to English. The same CQL file can be run by a computer and read by clinicians and reviewers.
How does CQL relate to FHIR?
CQL describes logic and is not tied to a data format; paired with FHIR, CQL can read values straight from FHIR health records. The CQL that CQL Workbench generates does not query FHIR itself: an evidence adapter checks the record first and passes in true, false or null for each condition as a parameter.
What is the difference between CQL and ELM?
CQL is the syntax people read and write; ELM (Expression Logical Model) is the structured form CQL compiles to, which execution engines run. CQL Workbench compiles CQL to ELM with cql-engine, then checks the result of every scenario.
Can I learn CQL without a programming background?
Yes. The CQL the workbench generates uses only a small part of the language: parameters, define, and, or, not and if. This page walks through one NHI reimbursement clause from start to finish, after which you can read the exported files.
Why write NHI reimbursement rules in CQL?
A written clause has to be interpreted by a person, and two people can read it differently. Written in CQL, the same record always gets the same result, and missing data is reported as insufficient data instead of quietly counting as ineligible.