SQL on FHIR WG Meetings
SQL on FHIR WG Meeting — February 10, 2026
Feb 10, 2026

Topics discussed:

  • The SQL in a query Library is base64-encoded, so where does the plain-text version live for humans reading the resource without tools? Reusing title was the original suggestion and it lost: it's semantically wrong, it misleads, and it creates a source-of-truth problem when the title and the encoded query drift apart. An extension won instead — uglier, but if you don't understand it you just drop it, which is the safe failure.
  • Dialect: keep it inside the content type, or pull it out into an extension? John's case for the extension is validation — there's a code system and value set for this, and a validator can check a binding but can't sensibly reach into a mime type to decide whether some string is a known dialect. Nikolai's case against is how implementations actually behave: you match the dialect string exactly (look for Postgres, and if it isn't there fall back to ANSI SQL and translate), so it functions as an atom, not a grammar anyone parses. The compromise: leave it in contentType, but extend the code system to enumerate whole mime types so the element can be bound and validated anyway.
  • Should a query declare its output schema through out parameters — the column names and types — so you can build a downstream query on it or house the results in a table? Nobody objected in principle and everyone agreed it would be optional. The difficulty is doing it comprehensively without leaning on SQL itself and walking straight back into dialect problems. Tabled until the operations settle and there's a worked example to pressure it against.
  • Why rowIndex rather than FHIRPath's $index: $index only has meaning inside a FHIRPath function iterating a collection, and SQL on FHIR does the opposite — it pulls the collection out and iterates it in a larger structure like forEach. At the root of a path expression $index refers to nothing. There's also a practical reason: FHIRPath engines let you inject % variables but not $ ones, which are special. Naming settled towards %rowIndex to avoid clashing with index. Still open: what the semantics of an index over an unordered set even are. The use cases are real though — primitive extensions match their values position by position, and once you've exploded nested data into rows you need an index at every point it goes multi-cardinality if you ever want to self-join and aggregate it back.
  • A long-running dog-fooding goal: get the IG publisher to actually run the ViewDefinition examples over a handful of sample resources and generate the output tables in the spec, rather than hand-written markdown tables that silently go stale. It would also keep the IG publisher's own implementation honest. It's blocked because the examples don't validate — URIs in the resource type, meta information the publisher doesn't know about — so it needs changes to the publisher itself and more back and forth to get there.