FQL

Field joins

Unlike like SQL, FQL has to deal with the fact that FHIR data does not come in the shape of a table. Yet, you need the same kind of output that an SQL statement would have produced. With SQL you know nested data will be in a different table, but with FHIR or any tree structured data, your nested data will often be part of the same resource. When you want to merge nested data in SQL, you use a table join, and logically, FQL needs a field based join.

Comparison to SQL

In tree shaped data Adding a subfield to your select-list, requires no extra syntax other than 'dotting' into the tree, where you would write this in SQL:

select
  id
  PatientName.given, 
from 
  Patient
  left join PatientName on PatientName.PatientId = Patient.id

In FQL you can simply write:

from Patient
select 
   id, 
   name.given

This is assuming you have to deal with only one name. If you have multiple names, the FHIRpath statement name.given will actually produce an array. In many cases, you don't want to have an array as a result value. To fix that, you can either only take the first name.given[0] or do a join between the Patient and the array of given, like this:

from Patient
select
  id,
  join name.given

Row ordering

Imagine you want to produce a row for your patients, with their name and identifier. But it's possible that they have more than one identifier, which in this case should produce a separate row, with each row having a unique identifier, but possible a repeated first and last name.

from Patient
select
    name.family,
    name.given[0],
    join identifier.value

Since the resulting row, is based on the resource, and the join is on a field inside that resource, you can place the join at any point in your select-list. So the following produces the same rows and values, just in a different order.

from Patient
select
    join identifier.value,
    name.family,
    name.given[0]

The order of the joins themselves of course do matter.

Joining on multiple values

It is quite common to join on more than one field. Especially when you want to join on several values of a sub node. A good example is Patient.name. To achieve a join over multiple values, you can use the unwrapping syntax (technically known as a group dereference):

from Patient
select
    identifier[0].value,
    join name { family, given[0] }

This query will produce a row per pair of name values, and repeating the identifier for each of those rows.

Inner Joins

The join keyword is actually a shorthand for inner join. So where ever you write join you can equally write inner join. An inner join produces a row when both sides of the join have a value.

For SQL that would produce rows where both tables 'meet', but with FQL, the meeting already took place, since you are joining with values or sub values of the current row. The effect of an inner join is therefore mostly visible in the sense that it will skip rows that has no values for the field after the join. If you want a row regardless, use the left join. See below:

Left joins

A left join differs from an inner join, in that it starts out with the left side table of the join. With FQL that means that a field join on a current row, will always show the current row, regardless of whether there are values in the join field.

Right joins

For field joins there is no such thing as a right join. The explanation under inner join helps to see why a right join on a field level has no meaning: if there is a field on the right side, there must be a row on the left side since the field is part of that row.