-
Notifications
You must be signed in to change notification settings - Fork 125
Subquery
Sub-queries are composed first:
// select film_id, title and rating from film table that have 'R' rating.
rRatingFilms :=
SELECT(
Film.FilmID,
Film.Title,
Film.Rating,
).FROM(
Film,
).WHERE(
Film.Rating.EQ(enum.MpaaRating.R),
).AsTable("rFilms")
AsTable("rFilms")
- allows SELECT statements to be used as a source table from FROM
clause.
To use sub-query columns in main SELECT statement expressions, we have to export column from sub-query,
using From
method.
rFilmId := Film.FilmID.From(rRatingFilms) // <- used for join condition
Now we can write:
query := SELECT(
rRatingFilms.AllColumns(),
Actor.AllColumns,
).FROM(
rRatingFilms.
INNER_JOIN(FilmActor, FilmActor.FilmID.EQ(rFilmID)).
INNER_JOIN(Actor, FilmActor.ActorID.EQ(Actor.ActorID)),
)
rRatingFilms.AllColumns(),
- all sub-query columns required for projection can be exported with AllColumns()
method.
The effect is the same as each of the sub-query columns are exported one by one using the From
method.
Debug SQL of above example:
SELECT "rFilms"."film.film_id" AS "film.film_id", -- <- the same alias names from sub-query
"rFilms"."film.title" AS "film.title",
"rFilms"."film.rating" AS "film.rating",
actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM (
SELECT film.film_id AS "film.film_id", -- <- the same alias names will appear in surrounding(main) query
film.title AS "film.title",
film.rating AS "film.rating"
FROM dvds.film
WHERE film.rating = 'R'
) AS "rFilms"
INNER JOIN dvds.film_actor ON (film_actor.film_id = "rFilms"."film.film_id")
INNER JOIN dvds.actor ON (film_actor.actor_id = actor.actor_id);
Note that column aliases are the same in main query as they are in the sub-query. This is because default column aliases are just passed from the sub-query to surrounding query, or in this case main query. Default column aliases are NOT affected with a sub-query alias(rFilms
).
Since the sub-query column aliases, are default table alias in the main query, we can use the generated model types as the scan destination, without any modifications:
var dest []struct {
model.Film // <- data from sub-query
Actors []model.Actor
}
err := stmt.Query(db, &dest)
The same logic applies as well for common table expression queries used in WITH statement or in other common table expression queries.
Let say destination for above sub-query is MyFilm
struct now:
type MyFilm struct {
Name string // <- !! there is no matching alias projection
Duration time.Duration // <- !!
Actors []model.Actor
}
var dest []MyFilm
Scan in this case would not work for Name
and Duration
destination fields, because there is no projection aliased as "my_film.name"
or "my_film.duration"
. To fix the scan we can add alias tag in our destination or we can update alias in the main query:
query := SELECT(
rRatingFilms.AllColumns().AS("my_film.*), // will change alias of all sub-query projections
Actor.AllColumns,
).
Debug SQL now contains correctly aliased projections for scan to work:
SELECT "rFilms"."film.film_id" AS "my_film.film_id", -- projections renamed from film.film_id to my_film.film_id
"rFilms"."film.title" AS "my_film.title",
"rFilms"."film.rating" AS "my_film.rating",
...
The list of sub-query projections can be easily reduced using the Except
method:
query := SELECT(
rRatingFilms.AllColumns().Except(Film.Title, Film.Rating),
Actor.AllColumns,
...
film.title
and film.rating
will not appear in the generated SQL anymore:
SELECT "rFilms"."film.film_id" AS "film.film_id",
actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
...
For instance:
customersPayments := SELECT(
Payment.CustomerID,
SUMf(Payment.Amount).AS("amount_sum"),
).FROM(
Payment,
).
GROUP_BY(Payment.CustomerID).
AsTable("customer_payment_sum")
customerId := Payment.CustomerID.From(customersPayments)
To export "amount_sum"
from customersPayments
sub-query we have to create column first with appropriate type and a name.
Since SUMf
returns float expression, we will create FloatColumn named "amount_sum"
and then export it from sub-query.
amountSum := FloatColumn("amount_sum").From(customersPayments)
This is only required if amount_sum
needs to appear in main query conditions (e.g. as part of JOIN or WHERE).
If this is not the case, amount_sum
can be exported in the usual way, using AllColumns()
method.
Lateral queries have a similar syntax as sub-queries and can be used in a FROM clause in the same way as tables or sub-queries.
languages := LATERAL(
SELECT(
Language.AllColumns,
).FROM(
Language,
).WHERE(
Language.Name.NOT_IN(String("spanish")).
AND(Film.LanguageID.EQ(Language.LanguageID)), // Film.Language is from main statement (bellow)
),
).AS("languages")
stmt := SELECT(
Film.FilmID,
Film.Title,
languages.AllColumns(),
).FROM(
Film,
languages,
).WHERE(
Film.FilmID.EQ(Int(1))
)
- Home
- Generator
- Model
- SQL Builder
- Query Result Mapping (QRM)
-
FAQ
- How to execute jet statement in SQL transaction?
- How to construct dynamic projection list?
- How to construct dynamic condition?
- How to use jet in multi-tenant environment?
- How to change model field type?
- How to use custom(or currently unsupported) functions and operators?
- How to use IN/NOT_IN with dynamic list of values?
- Scan stopped working after naming a destination type