SQL on FHIR WG Meetings
SQL on FHIR WG Meeting — November 4, 2025
Arjun Sanyal
Arjun Sanyal
Principal Antidote Solutions
Nikolai Ryzhikov
Nikolai Ryzhikov
CTO at Health Samurai
John Grimes
John Grimes
Principal Research Consultant CSIRO
Steve Munini
Steve Munini
CEO and CTO, Helios Software
Eugene Vestel
Eugene Vestel
Software Engineer
Nov 4, 2025

Topics discussed:

  • The motivation first. Nikolai explained there are two async patterns today — the legacy bulk export, and one documented in the spec that essentially nobody uses — and the documented one insists you respond with a Bundle, which doesn't suit a read that ought to return a resource, or an operation that ought to return Parameters. The goal is a generic pattern so any operation can be turned async, perhaps flagged in the OperationDefinition. As Nikolai put it, the one thing they definitely don't want is to produce a fifth pattern.
  • The disagreement is over one extra request. In Josh's draft, polling always answers 200 with a location; if the job failed, you only discover that after you fetch the result location. The justification is that it lets you tell a polling error apart from a job error. Nikolai isn't convinced the extra round trip earns that, and John put it more directly: if I already knew the job had failed, why send them fishing — I'd say so in the status. Steve noted bulk data export hands back a bundle with a collection of successes and a collection of failures, and that sometimes it's simply nice to be told everything is fine rather than sift a big bundle to find out. John also corrected a piece of the record: the claim that only one implementer uses the spec's existing async pattern isn't right — his side implemented it as a reusable piece and then offered it on everything, so this lands on top of something already in production.
  • Naming parameters in a SQL query turns out to have no standard answer. JDBC uses positional question marks; the Oracle-ish style uses :name; Steve went looking and found @name in the C# world and {0}/{1} elsewhere, both of which force you to keep the ordering straight. Colon is awkward because SQL constructs contain colons, so Nikolai floated handlebars as safest. John's point cuts across all of it: you need a parser regardless, because whatever symbol you pick can also appear inside a string literal. On table names specifically, John argued they must be distinguishable from ordinary parameters because you treat them differently — you go looking for them as a table. Arjun questioned why the table name needs to be dynamic at all; Nikolai's reason is that in most systems it isn't the real table name anyway, and might be another schema or a URL in a bucket.
  • Two problems with repeat. The FHIRPath expression in the spec is simply wrong: repeat returns a collection of strings and the count counts all of it, so it breaks as soon as there are two — exists or anyTrue were floated as the fix. And implementers reported getting repeat working via a common table expression; it runs in Postgres, slowly, and Nikolai wants to try Postgres JSON path with its recursive wildcard instead, which won't port but would prove the optimisation is there. John's open worry is whether any engine the group cares about lacks CTEs.
  • On Gene's CQL-to-SQL converter, Nikolai's advice was to split it into pieces and bank the valuable part first: reusing the clinical reasoning infrastructure to calculate measures over ViewDefinitions and queries, without any translation at all, is already interesting on its own. The translation itself he called a great adventure — he'd watched people translate to C for years and they're still mid-journey. He'd tried it twice himself: never got it automated, and by hand always ended up writing something more efficient. Which leaves the real question, how you prove the SQL means the same thing as the CQL — his answer being test datasets with expected results, so it stops mattering whether a human or a machine did the translating. John added the human-in-the-loop angle: what hooks does the tool give an author to review and feed back, so it augments rather than replaces?