INNER JOIN statement
Joining two tables is simple in Rell. We do this by putting all the tables we want to join in the FROM part of the at-expression and specify the constraint between them in the WHERE part:
function join_example() {
val res = (house, street) @* { street == house.street };
}
The Rell code above will correspond to the following SQL query:
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
Now, in this scenario, we have a foreign key
constraint on street
from house
, so we can access street
directly
from an instance of house
, for example:
val street = house_instance.street;
This will also correspond to a join statement:
SELECT A01."rowid"
FROM "house" A00
INNER JOIN "street" A01 ON A00."street" = A01."rowid"
WHERE A00."rowid" = ?
ORDER BY A00."rowid"
Similarly, accessing street
in any way when working with house
will result in a join.
house @ { .street.address == "FakeStreet" };
house @ {} (.street);
house_instance.to_struct();
Even though joining a table with a foreign key constraint may not be a heavy join, it is important to remember which
Rell statements will create a join. For example, we used the struct<house>
construct in the previous example in the
function expression. If house
has several references to other tables, the to_struct()
call will make several joins.
For example, let's add some references to the previous example:
entity garage {}
entity house_key {}
entity house {
...
garage;
house_key;
}
Avoiding unnecessary joins
If we, in this case, use the same construct as before:
val houses = house @* {} ($.to_struct());
This joins both garage
and house_key
entities. If these are not needed in the function, we should be more explicit
in the at-expression:
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 ) );
By changing the function's signature to is_mansion(house: house_info)
, we can optimize which values we get from the
database.
Extract all the values you need directly in the at expression to avoid unnecessary joins. It's best to define your own structures when needed.
Practical example
In this example we use a set of queries to retrieve filtered records based on the query specifics.
Add the get_house_with_streets
to the queries.rell
file located in the src/main
directory:
query get_houses_with_streets() = (house, street) @* { street == house.street } ( street = .street.address, .number );
Invoke the get_all_mansion
query using the chr
command in the CLI:
chr query get_houses_with_streets
[
[
"number": 23,
"street": "Drottninggatan"
],
[
"number": 24,
"street": "Kungsgatan"
],
[
"number": 30,
"street": "Kungsgatan"
]
]
Add the get_houses_with_specific_street
query to the queries.rell
file stored in the src/main
folder.
query get_houses_with_specific_street(street_address: text) = house @* { .street.address == street_address } ( .number, street = .street.address );
Invoke the get_houses_with_specific_street
query with the street argument using the chr
command:
chr query get_houses_with_specific_street "Drottninggatan"
This invocation results in the following response:
[
[
"number": 23,
"street": "Drottninggatan"
]
]
Next, we need to retrieve the specific house information such as number of rooms, floors and the total area by using the
get_houses_info
query.
Add the following code to the queries.rell
file stored in the src/main
directory:
query get_houses_info() = house @* { } ( house_info( .number_of_rooms, .number_of_floors, .floor_area ) );
chr query get_houses_info
The result of this query looks as follows:
[
[
"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
]
]