Chromia
Start Building

05 / 08

INNER JOIN statement

To join two tables in Rell, list every table you want to join in the FROM part of the at-expression, then state how they relate in the WHERE part:

rell
function join_example() {    val res = (house, street) @* { street == house.street };}

This Rell code maps to the following SQL query:

sql
SELECT A00."rowid", A01."rowid"    FROM "street" A00, "house" A01    INNER JOIN "street" A02 ON A01."street" = A02."rowid"    WHERE A00."rowid" = A02."rowid"    ORDER BY A00."rowid", A01."rowid"

Implicit statements

Here, house has a foreign key constraint on street. This means you can reach street straight from a house instance:

rell
val street = house_instance.street;

This access also produces a join:

sql
SELECT A01."rowid"    FROM "house" A00    INNER JOIN "street" A01 ON A00."street" = A01."rowid"    WHERE A00."rowid" = ?    ORDER BY A00."rowid"

The same applies whenever you touch street while working with house. Any such access results in a join.

rell
house @ { .street.address == "FakeStreet" };house @ {} (.street);house_instance.to_struct();

A join on a foreign key constraint is usually not expensive, but you should still know which Rell statements generate joins. The previous example used struct<house> in the function expression. When house references several other tables, calling to_struct() performs one join per reference. The next example adds a few references to illustrate this:

rell
entity garage {}entity house_key {}entity house {    ...    garage;    house_key;}

Avoiding unnecessary joins

Suppose you reuse the earlier construct:

rell
val houses = house @* {} ($.to_struct());

The query now joins both the garage and house_key entities. If the function doesn't need them, select the required fields explicitly in the at-expression:

rell
struct house_info {    number_of_rooms: integer;    number_of_floors: integer;    floor_area: integer;}val houses = house @* { } ( house_info( .number_of_rooms, .number_of_floors, .floor_area ) );

Changing the function signature to is_mansion(house: house_info) lets you control exactly which values are fetched from the database.

Practical example

The following queries return records filtered according to each query's criteria.

Add get_house_with_streets to the queries.rell file in the src/main directory:

src/main/queries.rell
query get_houses_with_streets() = (house, street) @* { street == house.street } ( street = .street.address, .number );

Run the get_houses_with_streets query from the CLI with the chr command:

shell
chr query get_houses_with_streets
result
[  [    "number": 23,    "street": "Drottninggatan"  ],  [    "number": 24,    "street": "Kungsgatan"  ],  [    "number": 30,    "street": "Kungsgatan"  ]]

Next, add the get_houses_with_specific_street query to queries.rell in the src/main folder.

src/main/queries.rell
query get_houses_with_specific_street(street_address: text) = house @* { .street.address == street_address } ( .number, street = .street.address );

Run get_houses_with_specific_street with the chr command, passing the street argument:

shell
chr query get_houses_with_specific_street "Drottninggatan"

You get the following response:

result
[  [    "number": 23,    "street": "Drottninggatan"  ]]

To fetch details for specific houses, such as the number of rooms, the number of floors, and the total area, use the get_houses_info query.

Add this code to queries.rell in the src/main directory:

src/main/queries.rell
query get_houses_info() = house @* { } ( house_info( .number_of_rooms, .number_of_floors, .floor_area ) );
shell
chr query get_houses_info

The query returns:

result
[  [    "floor_area": 4,    "number_of_floors": 4,    "number_of_rooms": 5  ],  [    "floor_area": 500,    "number_of_floors": 2,    "number_of_rooms": 12  ],  [    "floor_area": 700,    "number_of_floors": 3,    "number_of_rooms": 15  ]]
Written for this site from Chromia’s public course. Code samples are the course’s own.