---
{
  "title": "Métriques Da Vinci PAS avec SQL on FHIR dans Aidbox",
  "description": "Calcul des métriques opérationnelles Da Vinci PAS avec SQL on FHIR dans Aidbox.",
  "date": "2026-06-16",
  "author": "Akim Khalitov",
  "reading-time": "5 minutes",
  "tags": [
    "Analytics",
    "SQL on FHIR",
    "Prior Authorization"
  ],
  "tldr": "L'article montre comment calculer les métriques opérationnelles Da Vinci PAS pertinentes pour CMS-0057 directement sur les données FHIR d'autorisation préalable à l'aide de SQL on FHIR, sans avoir à construire un entrepôt de données distinct. Il présente les outils SQL on FHIR, les ViewDefinitions, les SQLViews et les SQLQueries, ainsi que le peu d'effort nécessaire pour créer ces artefacts, les regrouper en paquets, puis les matérialiser et les alimenter dans des outils de BI comme Metabase, Tableau ou Power BI."
}
---
L'autorisation préalable est un point de friction persistant dans le système de santé américain, et la
pression pour la mesurer ne cesse de croître : les payeurs veulent savoir où elle accroche, et
son signalement est en voie de devenir une exigence réglementaire. Cela signifie mesurer le volume de
soumissions, le taux d'erreur et le délai de décision.

Le [Da Vinci PAS IG](https://hl7.org/fhir/us/davinci-pas/) définit
[dix métriques opérationnelles](https://build.fhir.org/ig/HL7/davinci-pas/metrics.html)
et fournit un modèle logique
[`PASMetricData`](https://build.fhir.org/ig/HL7/davinci-pas/StructureDefinition-PASMetricData.html)
qui suggère les données sur lesquelles s'appuient ces métriques.

Leur calcul implique habituellement de copier les données dans un système d'analytique distinct et de
reconstruire chaque métrique à cet endroit — un travail personnalisé qui est refait à chaque site et qui
s'écarte de la source. Nous voulions l'inverse : définir chaque métrique une seule fois, l'exécuter au
plus près des données, et la lire avec les outils de BI déjà en place. Comme la transformation est
déclarative et demeure dans la base de données, aucune donnée n'est copiée à l'extérieur pour être
maintenue à jour et il n'y a pas de deuxième pile analytique à construire. Aidbox disposait déjà d'un
moyen d'y parvenir : le [SQL on FHIR](https://sql-on-fhir.org/ig/) intégré.

## SQL on FHIR en une minute

[SQL on FHIR](https://sql-on-fhir.org/ig/) standardise à la fois l'aplatissement et les
requêtes qui en découlent, au lieu que chaque fournisseur invente sa propre façon d'interroger des
ressources imbriquées. Vos métriques deviennent des artefacts FHIR portables et normalisés — une
analytique interopérable —
[expliquée en détail ici](https://www.health-samurai.io/articles/sql-on-fhir-interoperable-analytics).
Les composantes :

1. **[ViewDefinition](https://sql-on-fhir.org/ig/StructureDefinition-ViewDefinition.html)** :
   un format portable pour définir des vues tabulaires des données FHIR.
2. **[SQLQuery](https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/StructureDefinition-SQLQuery.html)**
   et **SQLView** : du SQL partageable sur ces vues, fondé sur la ressource FHIR
   [Library](https://hl7.org/fhir/R5/library.html). Un SQLQuery accepte des paramètres,
   par exemple une plage de dates de signalement ; un SQLView n'en a pas.
3. **[HTTP API](https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/index.html#http-api)** :
   opérations FHIR standard (`$viewdefinition-run`, `$sqlquery-run`) pour exécuter des vues
   et des requêtes.

Aidbox exécute tout cela contre le même Postgres qui sert l'API FHIR en production, de sorte que
les données et l'analytique résident dans une seule base de données.

## Cinq couches, du FHIR brut au tableau de bord

Les données PAS sont difficiles à aplatir. Elles sont dérivées du format X12, avec des réponses qui
arrivent sur plusieurs cycles, et des métriques qui s'étendent sur Claim, ClaimResponse, ainsi que le
prestataire et la couverture qui les entourent. Aucune vue unique ne fournit une ligne de métrique, donc
le pipeline est construit par couches, chacune lisant depuis celle du dessous, et le graphe de ces
couches constitue la traçabilité.

L'empilement de vues aplaties dans un modèle partagé, puis des métriques par-dessus, est le schéma
analytique général décrit en détail dans
[SQL on FHIR: Interoperable Analytics](https://www.health-samurai.io/articles/sql-on-fhir-interoperable-analytics) ;
nous l'appliquons ici à PAS :

<div class="narrow" style="display: flex; justify-content: center">

```mermaid
flowchart TD
  L1["Layer 1: Raw FHIR<br/>Claim, ClaimResponse, Practitioner, Coverage"]
  L2["Layer 2: ViewDefinitions<br/>flatten each resource into a table"]
  L3["Layer 3: SQLViews<br/>cross-resource joins"]
  L4["Layer 4: Metric model<br/>PASMetricDataView"]
  L5["Layer 5: Metric queries<br/>one SQLQuery per metric"]
  BI["Any BI tool / dashboard"]
  L1 --> L2 --> L3 --> L4 --> L5 --> BI
```

</div>

Le IG définit dix métriques :

<div class="narrow">

| Métrique | Ce qu'elle mesure |
|----------|-------------------|
| 1. volume de soumissions | Volume de soumissions PAS (en tant que 278 et éléments de ligne) |
| 2. mises à jour, annulations, requêtes | Volume des mises à jour, annulations et requêtes PAS |
| 3. requêtes de prestataire non prescripteur | Volume des requêtes émanant d'un prestataire autre que le prescripteur |
| 4. taux d'erreur | % des soumissions PAS retournant une erreur (par type et par payeur) |
| 5. résultat final à la soumission initiale | % des soumissions PAS retournant un résultat final à la soumission initiale (tout élément et tous les éléments) |
| 6. volume de mises en attente et résolution | Volume des éléments de ligne ayant une mise en attente initiale (PEND) et nombre de mises en attente résolues, ainsi que (plus complexe) le délai moyen de résolution de chaque mise en attente |
| 7. délai jusqu'au résultat final | Délai total depuis la soumission initiale jusqu'au résultat final de l'autorisation préalable pour tous les éléments de ligne |
| 8. segmentation | Tout ce qui précède par payeur / prestataire (selon la métrique) et dans le temps |
| 9. demandes en attente | Demandes PAS en suspens |
| 10. vieillissement des mises en attente | Vieillissement des demandes en attente (PENDED) |

</div>

Nous examinerons la première métrique, le volume de soumissions, car elle est la plus simple. Nous la
traçons couche par couche, en commençant à la Couche 1, les ressources brutes.

**Couche 1 :** prenons deux demandes de préautorisation, `c1` avec deux éléments de ligne et `c2` avec
un. Voici `c1` en JSON imbriqué :

```json
{
  "resourceType": "Claim",
  "use": "preauthorization",
  "created": "2026-02-01",
  "provider": { "reference": "Practitioner/p1" },
  "item": [ { "sequence": 1, ... }, { "sequence": 2, ... } ]
}
```

**Couche 2 :** une ViewDefinition effectue l'aplatissement. Un `where` conserve les demandes de
préautorisation, et `forEach` sur `item` transforme le tableau en une ligne par élément. Chaque
colonne est une expression FHIRPath — `getResourceKey()` et `getReferenceKey()` pour les
identifiants et les références, les chemins ordinaires pour le reste, même les valeurs enfouies
profondément dans les extensions PAS, sans analyse syntaxique personnalisée :

```json
{
  "resourceType": "ViewDefinition",
  "name": "ClaimView",
  "resource": "Claim",
  "where": [{ "path": "use = 'preauthorization'" }],
  "select": [
    { "column": [
      { "name": "claim",    "path": "getResourceKey()" },
      { "name": "created",  "path": "created" },
      { "name": "provider", "path": "provider.getReferenceKey()" }
    ]},
    { "forEach": "item", "column": [{ "name": "item", "path": "sequence" }] }
  ]
}
```

Pour les deux demandes, cela donne une ligne par élément :

<div class="narrow">

| claim | created    | provider | item |
|-------|------------|----------|------|
| c1    | 2026-02-01 | p1       | 1    |
| c1    | 2026-02-01 | p1       | 2    |
| c2    | 2026-02-01 | p1       | 1    |

</div>

**Couche 3 :** une SQLView, `ClaimWithProviderView`, joint `ClaimView` avec `PractitionerView`
pour ajouter le NPI du prestataire :

```sql
-- depends on: ClaimView, PractitionerView
select c.*, pr.npi as provider_npi
from "ClaimView" c
left join "PractitionerView" pr on pr.id = c.provider
```

ce qui donne :

<div class="narrow">

| claim | created    | provider | item | provider_npi |
|-------|------------|----------|------|--------------|
| c1    | 2026-02-01 | p1       | 1    | 199...       |
| c1    | 2026-02-01 | p1       | 2    | 199...       |
| c2    | 2026-02-01 | p1       | 1    | 199...       |

</div>

**Couche 4 :** le modèle.

Le côté réponse, `ResponseResultView`, est construit comme le côté demande :
une ViewDefinition aplatit chaque élément de `ClaimResponse` et une SQLView expose ses champs
`claim`, `item` et `result`. `PASMetricDataView` fait une jointure externe gauche de chaque
élément de demande avec sa réponse, de sorte que chaque ligne porte à la fois la demande et son
résultat — le modèle PASMetricData réalisé ici sous la forme d'une ligne par élément de ligne :

```sql
-- depends on: ClaimWithProviderView, ResponseResultView
select c.claim   as exchange_id,
       c.item    as item_sequence,
       c.created as request_time,
       r.result
from "ClaimWithProviderView" c
left join "ResponseResultView" r
  on r.claim = c.claim and r.item = c.item
```

ce qui donne le modèle partagé sur lequel toutes les métriques s'exécutent :

<div class="narrow">

| exchange_id | item_sequence | request_time | result   |
|-------------|---------------|--------------|----------|
| c1          | 1             | 2026-02-01   | approved |
| c1          | 2             | 2026-02-01   | pended   |
| c2          | 1             | 2026-02-01   | approved |

</div>

**Couche 5 :** le volume de soumissions est un agrégat sur ce modèle, un SQLQuery. Il s'agit
d'une métrique d'adoption, donc il compte les soumissions et les éléments par année de signalement,
qu'un tableau de bord peut filtrer selon l'année souhaitée :

```sql
-- SubmissionVolume, by reporting year
-- depends on: PASMetricDataView
select extract(year from request_time)::int as reporting_year,
       count(distinct exchange_id) as submission_count,
       count(*) as service_item_count
from "PASMetricDataView"
group by reporting_year
```

En tant que ressource SQLQuery, il porte une URL canonique, sa dépendance `depends-on`
(`PASMetricDataView`) et le SQL. L'emplacement des paramètres est vide ici, mais c'est là qu'un
filtre comme une plage de dates de signalement pourrait être branché :

<div class="narrow" style="display: flex; justify-content: center">

<img src="submission-volume-query.avif" alt="SubmissionVolume as a SQLQuery in the Aidbox builder: a canonical URL, a depends-on dependency on PASMetricDataView, an empty parameters slot, and the aggregate SQL." width="520" />

</div>

En l'exécutant sur nos deux demandes, toutes deux de 2026 : deux échanges distincts, c1 et c2, donc
2 soumissions, et trois lignes d'éléments, donc 3 éléments de service :

<div class="narrow">

| reporting_year | submission_count | service_item_count |
|----------------|------------------|--------------------|
| 2026           | 2                | 3                  |

</div>

## La traçabilité en prime

Dans la plupart des piles technologiques, la traçabilité est un travail supplémentaire : un outil
distinct, ou un diagramme que quelqu'un maintient à jour manuellement. Aidbox la dérive
automatiquement. Parce que chaque SQLView et SQLQuery nomme ses entrées avec
`relatedArtifact: depends-on`, dès le chargement des artefacts, Aidbox connaît le graphe de
dépendances et le dessine, depuis le type de ressource brute jusqu'à la métrique.

Chaque nœud s'exécute en place, de sorte que le débogage est concret : exécutez une seule
ViewDefinition, SQLView ou SQLQuery de manière autonome, voyez ce qu'elle retourne, et remontez
un chiffre d'un graphique jusqu'au FHIRPath qui l'a produit.

Voici la traçabilité qu'Aidbox dessine pour la métrique du volume de soumissions. `Claim`,
`Practitioner` et `ClaimResponse` sont aplatis en ViewDefinitions, qui alimentent les SQLViews
`ClaimWithProviderView` et `ResponseResultView`, puis le modèle `PASMetricDataView`, puis la
requête `SubmissionVolume` :

![Aidbox lineage graph for the submission volume metric: Claim, Practitioner, and ClaimResponse flatten into ViewDefinitions, which feed the ClaimWithProviderView and ResponseResultView SQLViews, then the PASMetricDataView model, then the SubmissionVolume query.](lineage-graph.avif)

Le véritable graphe PAS est plus grand : le même modèle (`PASMetricDataView`) alimente les neuf
autres métriques, chacune déclarant ses entrées de la même façon.

## Exécutable partout, compatible avec tous les outils de BI

`$sqlquery-run` exécute une métrique immédiatement sur les données en production, ce qui est
pratique pendant la construction ou pour répondre à une question ponctuelle.

Pour les tableaux de bord, chaque métrique devient une vue matérialisée Postgres, précalculée et
actualisée en place. Metabase, Tableau ou Power BI se connectent à une table ordinaire et ne voient
jamais le FHIR sous-jacent. Rien n'est exporté et il n'y a pas d'entrepôt à synchroniser ;
l'analytique réside dans le même Postgres que les données FHIR.

## Ce que vous obtenez au final

Les métriques se retrouvent sous la forme d'une poignée de ressources FHIR ordinaires —
**ViewDefinitions**, **SQLViews** et **SQLQueries** — et non d'un pipeline personnalisé. Cela
convient bien à PAS, qui est encore en évolution : PASMetricData est une forme de référence plutôt
qu'une forme fixe, et les métriques qui l'entourent continuent de changer. Quand elles changent,
vous modifiez un artefact et le reste du graphe suit. Rien de tout cela n'est spécifique à PAS non
plus : tout IG qui définit des métriques, ou simplement un modèle logique, peut être construit de
la même façon.

Comme ce sont des ressources, elles se versionnent, se regroupent dans un paquet et s'installent
en un seul téléversement. Dans la vue de traçabilité, vous pouvez exécuter, déboguer ou modifier
n'importe quel nœud de façon ponctuelle, et `$sqlquery-run` exécute la tranche que vous lui
désignez : une seule vue, le chemin d'une métrique jusqu'à la source, ou l'ensemble du modèle
d'un coup.

> Vous souhaitez exécuter des métriques PAS sur vos propres données? Essayez une vue dans le
> [générateur de ViewDefinition](https://sqlonfhir.aidbox.app/), ou construisez le pipeline
> complet vous-même dans [Aidbox](https://www.health-samurai.io/aidbox).
>
> Les payeurs qui font face aux exigences de signalement d'autorisation préalable de CMS-0057-F
> peuvent obtenir la prise en charge de Da Vinci PAS, prête à déployer, avec
> [Payerbox](https://www.health-samurai.io/cms-0057-f), la solution CMS-0057-F de Health Samurai.
>
> [Parlez à notre équipe](https://www.health-samurai.io/company#contact-form) pour commencer.

### Voir aussi :
- [SQL on FHIR: Interoperable Analytics](https://www.health-samurai.io/articles/sql-on-fhir-interoperable-analytics), le schéma général à la base de ce pipeline

## Références

- [Da Vinci PAS IG](https://hl7.org/fhir/us/davinci-pas/) et sa
  [page de métriques](https://build.fhir.org/ig/HL7/davinci-pas/metrics.html)
- [Modèle logique PASMetricData](https://build.fhir.org/ig/HL7/davinci-pas/StructureDefinition-PASMetricData.html)
- [Spécification SQL on FHIR](https://sql-on-fhir.org/ig/) et la
  [ressource ViewDefinition](https://sql-on-fhir.org/ig/StructureDefinition-ViewDefinition.html)
- [« SQL on FHIR: Interoperable Analytics », Nikolai Ryzhikov](https://www.health-samurai.io/articles/sql-on-fhir-interoperable-analytics)
- [« SQL on FHIR: tabular views of FHIR data using FHIRPath », npj Digital Medicine](https://www.nature.com/articles/s41746-025-01708-w)
- [FHIRPath](https://hl7.org/fhirpath/)
- [Générateur de ViewDefinition](https://sqlonfhir.aidbox.app/)