Skip to main content

Relationships in SQRL

SQRL supports relationship columns which relate a row in one table to rows in another table.

Users.purchases := JOIN Orders ON Orders.customerid = @.id;

This statement defines a relationship column purchases on the Users table that relates each user record to rows in the Orders table where the customerid is equal to the user id. In other words, purchases relates users to their orders.

A relationship is defined via a JOIN expression in standard SQL syntax. Relationships are defined as localized queries which means the JOIN expression on the right-hand side of the statement is interpreted for each row of the table on which the relationship is defined and the at-sign @ is used to refer to each row.

Relationship columns are a convenient way to make relationships in the data explicit, simplify joins, and allow consumers of the data API to navigate through the data.

JOINs

Relationships can be used in FROM and JOIN clauses of queries which makes complex join-queries easier to read.

Users.spending := SELECT endOfWeek(p.time) AS week,
sum(t.price) AS spend, sum(t.saving) AS saved
FROM @.purchases p JOIN p.totals t
GROUP BY week ORDER BY week DESC;

This statement defines a nested table spending underneath Users which aggregates over the nested order totals for all purchases of each user. Relationships used in FROM and JOIN are expanded to their original definition. That means, FROM @.purchases gets expanded to FROM @ JOIN Orders p ON p.customerid = @.id.

Relationships are particularly useful in the context of nested tables because they make it easy to navigate between parent and child. In the Quickstart tutorial we define the nested Orders.totals table as an aggregation over all items.

When defining a nested table, each child row has a relationship column parent to relates the child row to its parent row. Likewise, the parent table has a relationship column with the name of the nested table that provides access to all child rows of the parent.

Hence, we are able to use JOIN p.totals to join the totals for each order by following the totals relationship on the order row.

Relationship Expressions

Relationships can be used inside expressions in a query.

UsersWithSpending := SELECT id, email, order_stats.spend AS spend 
FROM Users;

This statement defines a new table UsersWithSpending that combines user information with their total spending from the nested order_stats table we defined in the intro tutorial.

Using relationships in expressions simplifies the query by eliminating a join.

Moreover, a relationship expression can be used to pull in optional data. UsersWithSpending contains one row per user and order_stats.spend evaluates to null if a user does not have any orders.

Contrast this with the following table definition that references the spending through an explicit join on the relationship:

UsersWithSpending := SELECT u.id, u.email, s.spend 
FROM Users u JOIN u.order_stats s;

This table contains only rows for users who placed an order since the JOIN on u.order_stats makes it required.

For SQL nerds: Relationships expressions are expanded to left-joins.

Relationship expression can also be used in WHERE clauses to filter out data.

HighSpendingUsers := SELECT id, email FROM Users 
WHERE order_stats.spend > 1000;

This table contains user information for those users who have spent more than a thousand dollars in total.

API Access

The relationships defined in a SQRL script can be exposed in the resulting data API to give consumers of the API the ability to navigate through the data and retrieve related records. Relationships make it possible to represent complex data efficiently through the API.

To learn more about how to expose and navigate relationships in the data API, refer to the API documentation.

Multiplicity

Relationships are defined as SQL JOIN clauses with the at-sign @ used to refer to the table on which the relationship is defined.

In addition, we can add an ORDER BY and LIMIT clause to the relationship.

Users.recent_purchases := JOIN Orders ON Orders.customerid = @.id ORDER BY Orders.time LIMIT 10;

This statement defines the relationship column recent_purchases similar to the purchases relationship above but orders the related Orders row by time and restricts the number of related rows to 10.

The ORDER BY clause is used to make the order in which the related rows are returned in the API explicit.

The LIMIT clause is used to restrict the multiplicity of the relationship.

Users.last_purchase := JOIN Orders ON Orders.customerid = @.id LIMIT 1;

This statement defines a relationship with 0..1 multiplicity.