Das Problem bei der Ausführung von eCQMs
Electronic Clinical Quality Measures (eCQMs) sind das Instrument, mit dem Programme die Versorgungsqualität messen – ob berechtigte Patienten ihr Darmkrebsscreening erhalten haben, ob ihr Bluthochdruck kontrolliert ist. Die CMS- und HEDIS-Maßnahmen, über die jedes wertbasierte Versorgungsprogramm berichtet, werden in CQL (Clinical Quality Language) verfasst, und der übliche Weg zur Ausführung ist eine dedizierte CQL-Engine: eine separate Laufzeitumgebung, die die Maßnahme analysiert, für Terminologie und Patientendaten auf Ihren FHIR-Server zurückgreift und einen MeasureReport zurückgibt.
Das funktioniert gut, aber mit Kosten:
- Eine separate Rechenebene. Eine CQL-Engine ist eine eigene Laufzeitumgebung – Sie deployen und skalieren sie neben Ihrem FHIR-Server, und sie führt die Populationsberechnung außerhalb der Datenbank durch. Das ist echte Infrastruktur und ein Skalierungsproblem bei großen Populationen.
- Schwer erklärbar. Wenn ein Patient als Versorgungslücke auftaucht, können Sie einem Kliniker genau zeigen, warum? Das bedeutet, Engine-Interna und ValueSet-Expansionen zu verfolgen – nicht eine Abfrage zu lesen.
Hier ist also die Frage, die dieser Beitrag beantwortet: Was wäre, wenn die Maßnahmenlogik einfach SQL wäre, das dort ausgeführt wird, wo Ihre Daten bereits liegen?
Das stellt sich als möglich heraus – und das Ergebnis ist mehr als ein Performance-Trick: Mit SQL on FHIR wird das gesamte Wissen zur Berechnung einer Maßnahme zu portablen, standardkonformen FHIR-Artefakten, die Sie in einem FHIR-Paket bündeln und auf einem anderen Server installieren können. Dies ist eine technische Schritt-für-Schritt-Erklärung, wie das funktioniert – mit einem vollständigen Arbeitsbeispiel, das Sie klonen und ausführen können.
Die Idee: Maßnahmen sind Mengenlogik, und SQL ist eine Mengensprache
Eine Qualitätsmaßnahme ist im Wesentlichen Mengenarithetik über eine Patientenpopulation:
- Anfangspopulation (Initial Population) – wer ist berechtigt (Alter, Begegnungen, eine Erkrankung).
- Nenner / Ausschlüsse (Denominator / Exclusions) – wer wird gezählt, abzüglich wer herausgenommen wird (Hospiz, Palliativversorgung, Gebrechlichkeit…).
- Zähler (Numerator) – wer die Maßnahme erfüllt hat (ein Screening, ein kontrollierter Wert).
- Ergebnis (Score) –
Zähler / (Nenner − Ausschlüsse).
Jede dieser Gruppen ist eine Menge von Patienten. SQL ist sehr gut darin, mit Mengen zu arbeiten. Das Einzige, was zwischen FHIR-Daten und einer SQL-Abfrage steht, ist, dass FHIR-Ressourcen tief verschachteltes JSON sind und Terminologiezugehörigkeit („ist dieser Code im ValueSet?") keine Spalte ist, nach der man filtern kann. SQL on FHIR löst beides.
Ebene 1: FHIR mit ViewDefinitions in Tabellen abflachen
Eine ViewDefinition ist eine FHIR-Ressource, die beschreibt, wie ein Ressourcentyp in eine flache Tabelle umgewandelt wird. Wenden Sie eine auf Encounter an, materialisieren Sie sie, und Sie erhalten eine übersichtliche Tabelle sof.encounter_flat mit patient_id, type_system, type_code, status, period_start und so weiter – Ihre Maßnahmenlogik liest einfache Spalten, anstatt sich durch verschachtelte FHIR-Strukturen zu arbeiten.
Das Beispiel enthält eine ViewDefinition pro Ressourcentyp, den die Maßnahmen verwenden – Patient, Encounter, Condition, Observation, Procedure und einige mehr – jede in das Schema sof.* materialisiert und über eine dünne Wrapper-View bereitgestellt. Diese gemeinsam genutzte flache Ebene wird von jeder Maßnahme wiederverwendet.
Ebene 2: Terminologie als JOIN, nicht als $expand
Der andere schwierige Teil einer Maßnahme ist die Code-Menge-Zugehörigkeit: „Ist dieser Encounter einer der sieben Arten qualifizierender Besuche?" In einer CQL-Engine-Konfiguration wird dies typischerweise zur Laufzeit gegen einen Terminologiedienst aufgelöst. Bei SQL on FHIR flachen Sie die ValueSet-Expansionen einmalig in eine concepts-Tabelle ab – eine Zeile pro (valueset_url, system, code) – und die Zugehörigkeit wird zu einem gewöhnlichen Join:
JOIN concepts c
ON c.system = e.type_system
AND c.code = e.type_code
AND c.valueset_url = 'http://cts.nlm.nih.gov/fhir/ValueSet/…'
Kein Netzwerk-Roundtrip, keine patientenweise Expansion. Das ValueSet wird offline expandiert und indiziert, sodass die Prüfung „Zählt der Code dieses Patienten?" mengenbasiert und schnell ist.
Ebene 3: Die Maßnahme ist eine einzige SQL-Abfrage
Mit flachen Tabellen und einem verfügbaren concepts-Join wird eine gesamte Maßnahme zu einer einzigen Abfrage aus CTEs – und sie liest sich bemerkenswert nah an der englischsprachigen Definition der Maßnahme. Hier ist die Anfangspopulation von CMS130 (Darmkrebsscreening) – Patienten im Alter von 46–75 Jahren mit einem qualifizierenden Encounter während des Messzeitraums:
WITH mp AS (
SELECT '2026-01-01'::timestamptz AS mp_start,
'2026-12-31'::timestamptz AS mp_end
),
qualifying_encounters AS (
SELECT DISTINCT e.patient_id
FROM encounter_flat e
JOIN concepts c
ON c.system = e.type_system
AND c.code = e.type_code
AND c.valueset_url IN (
-- ValueSet URLs shortened for readability; full URLs in the example repo
'http://cts.nlm.nih.gov/fhir/ValueSet/…', -- Office Visit
'http://cts.nlm.nih.gov/fhir/ValueSet/…', -- Annual Wellness Visit
'…' -- + 5 more
)
CROSS JOIN mp
WHERE e.status = 'finished'
AND e.period_start BETWEEN mp.mp_start AND mp.mp_end
),
initial_population AS (
SELECT p.id AS patient_id
FROM patient_flat p
CROSS JOIN mp
WHERE EXTRACT(YEAR FROM AGE(mp.mp_end, p.birth_date::date)) BETWEEN 46 AND 75
AND p.id IN (SELECT patient_id FROM qualifying_encounters)
)
-- … denominator exclusions, numerator, and the final score follow as more CTEs
Der Zähler fügt einige weitere CTEs hinzu (Koloskopie innerhalb von 9 Jahren, FOBT während des Zeitraums usw.), Ausschlüsse vereinen Hospiz/Palliativversorgung/Gebrechlichkeit, und ein abschließendes SELECT berechnet das Ergebnis. Der Kernpunkt: Die Maßnahme ist lesbar. Sie können sie lesen, mit der Spezifikation vergleichen und SELECT * FROM initial_population ausführen, um genau zu sehen, wer sich qualifiziert hat.
Gemeinsam genutzte Logik bleibt gemeinsam genutzt
Ausschlüsse wie Hospiz, Palliativversorgung und schwere Erkrankungen mit Gebrechlichkeit kommen in vielen Maßnahmen vor. Im Beispiel werden sie zu wiederverwendbaren Bausteinen ausgelagert statt kopiert, sodass eine Korrektur der Hospiz-Logik sie überall gleichzeitig behebt.
Aber wer schreibt das ganze SQL?
Der offensichtliche Einwand: Die manuelle Übersetzung des CQL einer Maßnahme in SQL klingt nach viel sorgfältiger, fehleranfälliger Arbeit – und es gibt Dutzende von eCQMs. In der Praxis ist dies genau die Art von Aufgabe, die moderne KI-Coding-Assistenten gut bewältigen. CQL und SQL sind beide strukturierte, gut spezifizierte Sprachen, und die Logik einer Maßnahme lässt sich sauber auf das oben gezeigte CTE-Muster abbilden (Anfangspopulation → Ausschlüsse → Zähler → Ergebnis).
Das ist keine Hoffnung – so wurde das Beispiel selbst erstellt. Jede Maßnahme wurde von ihrem veröffentlichten CQL mit einem KI-Assistenten in SQL übersetzt, dann patient-für-patient gegen die MeasureReport-Referenzfixtures von CMS verifiziert, bis die Zahlen exakt übereinstimmten. Die KI übernimmt die mechanische Übersetzung; die Fixtures halten sie ehrlich.
Die Referenzfixtures machen die KI-gestützte Übersetzung vertrauenswürdig: Erwartete MeasureReport-Ergebnisse für die Testpatienten werden zusammen mit dem Maßnahmeninhalt im Repository dqm-content-qicore-2025 veröffentlicht, sodass jede übersetzte Maßnahme gegen die Grundwahrheit überprüfbar ist – und nicht auf Treu und Glauben akzeptiert werden muss.
Standardkonformität sicherstellen: SQLQuery + Measure/$evaluate-measure
SQL intern auszuführen ist in Ordnung, aber das Ziel ist ein konformer FHIR-Dienst, kein Datenbankskript. Zwei SQL-on-FHIR-Bausteine schließen diese Lücke:
- Das SQL jeder Maßnahme wird in Aidbox als SQLQuery-
Library-Ressource (ein SQL-on-FHIR-Profil aufLibrary) gespeichert und mit der Operation$sqlquery-runaufgerufen. Die Berechnungslogik lebt im FHIR-Server, als erstklassige Ressourcen – nicht im Anwendungscode. - Der Dienst beantwortet die Standard-FHIR-R4-Operation
Measure/$evaluate-measureund gibt einen korrektenMeasureReportzurück – sodass jeder FHIR-Client ihn genauso verarbeitet wie ein Ergebnis einer CQL-Engine.
Jede SQLQuery Library deklariert auch depends-on-Herkunft zu den ViewDefinitions, die sie liest, und gibt Ihnen einen abfragbaren Graphen: Maßnahme → Views → Ressourcen. Alles, was eine Maßnahme benötigt – die flachen Views, die Terminologie, die Berechnung – wird nun als standardmäßige FHIR-Ressourcen ausgedrückt, die im FHIR-Server leben. Das bereitet das eigentliche Ergebnis vor.
Die evaluate-measure-App in der Mitte enthält keine Maßnahmenlogik: Sie ermittelt die richtige SQLQuery Library, ruft $sqlquery-run auf und formt die zurückgegebenen Zeilen in einen MeasureReport um. Die Berechnung selbst läuft in der Datenbank.
Das Ergebnis: Die gesamte Maßnahmen-Suite ist ein portables FHIR-Paket
Da jeder Teil einer Maßnahme nun eine standardmäßige FHIR-Ressource ist, lässt sich die gesamte Suite in ein einziges FHIR-NPM-Paket packen: Terminologie (CodeSystems + ValueSets), die ViewDefinitions, die die Daten abflachen, und die SQLQuery Libraries, die die Berechnungslogik enthalten. Im Beispiel trägt dieses Paket 170 Ressourcen über ein Dutzend Maßnahmen – 10 CodeSystems, 107 ValueSets, 10 ViewDefinitions und 43 SQLQuery Libraries.
Das Paketformat ist Standard – dasselbe FHIR-NPM-Format, in dem Implementation Guides ausgeliefert werden – sodass jeder FHIR-Server es mit seinem eigenen Paketinstallationsmechanismus laden kann; in Aidbox ist das ein $fhir-package-install-Aufruf beim Serverstart, und die Definitionen sind einfach vorhanden. Und hier ist der entscheidende Punkt: Dieses Paket ist nicht an Aidbox gebunden. Es sind standardmäßige SQL-on-FHIR-Bausteine – ViewDefinitions und SQLQuery Libraries, die durch die SQL on FHIR-Spezifikation definiert sind. Übertragen Sie das Paket auf einen beliebigen FHIR-Server, der diese Bausteine implementiert, installieren Sie es, und dieselben Maßnahmen werden auf dieselbe Weise berechnet. Wie jeder Server sie ausführt, ist ein Implementierungsdetail – einige betreiben eine separate SQL-on-FHIR-Engine, andere führen in der Datenbank aus.
Aidbox wählt den datenbankinternen Weg: Die ViewDefinitions werden in Tabellen oder Views materialisiert, und die SQLQuery Libraries werden als natives SQL ausgeführt – direkt in seinem PostgreSQL –, genau dort, wo die Daten bereits liegen. Das Paket installiert und läuft also ohne zusätzliche Infrastruktur für die Berechnung selbst: keine Ausführungs-Engine zum Deployen, Skalieren oder Synchronisieren. Die Maßnahmenlogik verteilt sich genauso wie ein Implementation Guide – als standardmäßige, teilbare FHIR-Artefakte.
Ein Ergebnis untersuchen
Erinnern Sie sich an das „schwer erklärbar"-Problem? Es erhält hier eine direkte Antwort. Fragen Sie nach einem einzelnen Patienten, und der MeasureReport enthält ein evaluatedResource-Array: echte FHIR-Ressourcenreferenzen, jede mit der Population markiert, die sie erfüllt, über die Standard-Extension cqf-criteriaReference.
"evaluatedResource": [
{
"reference": "Encounter/abc",
"extension": [
{ "url": ".../cqf-criteriaReference", "valueString": "initial-population" },
{ "url": ".../cqf-criteriaReference", "valueString": "denominator" }
]
},
{
"reference": "Procedure/xyz",
"extension": [
{ "url": ".../cqf-criteriaReference", "valueString": "numerator" }
]
}
]
„Warum befindet sich dieser Patient im Zähler?" – diese Procedure. „Warum ausgeschlossen?" – dieser Hospiz-Encounter. Jede Referenz löst sich zu einer Ressource auf, die im Server vorhanden ist, sodass der nächste Schritt der Untersuchung einfach ein GET ist.
Für die Untersuchung auf Populationsebene enthält jede Maßnahme auch eine SQLQuery Library mit dem Suffix -evidence: eine Zeile pro Patient mit der vollständigen Entscheidungskette – welcher Pfad den Zähler erfüllte (Koloskopie, FOBT oder keins), die auslösende Ressource mit ihrem Code und Datum sowie welcher Ausschluss griff. Das ist eine Liste von Versorgungslücken – wer fehlt beim Screening, und was genau fehlt – als eine einzige Abfrage. Die Demo-App des Beispiels macht diese Abfragen anklickbar: eine maßnahmenübergreifende Arbeitsliste, eine patientenbezogene 360-Grad-Ansicht mit Evidence-Drill-Down und exportierbare Outreach-Listen.

Und wenn eine Zahl immer noch falsch aussieht, ist jede Population ein benanntes CTE: SELECT * FROM initial_population und Schritt für Schritt einschränken – keine Engine-Interna zum Verfolgen.
Selbst ausprobieren
Alles oben Beschriebene ist ein funktionierendes Open-Source-Beispiel im Aidbox-Beispiel-Repository – ein Dutzend CMS-Maßnahmen (CMS130, CMS165, CMS125, CMS131 und mehr) mit Beispielpatientendaten und einer interaktiven Demo-App.
→ github.com/Aidbox/examples · aidbox-custom-operations/measure-evaluate
Folgen Sie den Anweisungen in der README, um den gesamten Stack lokal auszuführen: Starten Sie Aidbox mit dem beim Start installierten Maßnahmenpaket, laden Sie den Beispieldatensatz, berechnen Sie die Maßnahmen über die Standard-Operation Measure/$evaluate-measure und erkunden Sie die Evidence jedes Patienten in der Demo-App-Benutzeroberfläche. Was zurückkommt, ist ein einfacher FHIR-MeasureReport mit den Populationszahlen und dem Ergebnis – berechnet durch SQL, innerhalb von Aidbox, ohne CQL-Engine irgendwo im Stack.

Fazit
CQL ist eine gute Authoring-Sprache für Qualitätsmaßnahmen. Aber sie muss nicht Ihre Ausführungs-Engine sein. Wenn Sie FHIR mit ViewDefinitions abflachen, Terminologie in einen Join umwandeln und jede Maßnahme als SQLQuery Library hinter Measure/$evaluate-measure ausdrücken, hört die Maßnahmenlogik auf, in einer Engine eingesperrt zu sein, und wird zu dem, was SQL on FHIR verspricht: standardmäßige FHIR-Artefakte, die Sie einmal paketieren und überall ausführen können. Liefern Sie die gesamte Suite als FHIR-Paket aus, installieren Sie es auf einem beliebigen SQL-on-FHIR-Server, und dieselben Maßnahmen werden auf dieselbe Weise berechnet – nachvollziehbar bis zur einzelnen Ressource. Auf Aidbox laufen sie nativ in PostgreSQL, sodass überhaupt keine separate Ausführungs-Engine zu betreiben ist. Und dorthin zu gelangen ist zugänglicher als es klingt: KI-Assistenten übersetzen das CQL in SQL, und die eigenen Referenzfixtures von CMS verifizieren jede Maßnahme gegen erwartete Ergebnisse.
Interessiert daran, diesen Ansatz zur Maßnahmenberechnung mit Ihren eigenen Daten auszuprobieren? Kontaktieren Sie uns – wir erläutern Ihnen den Prozess gerne im Detail.





