jet/wiki/SELECT.md
2019-07-04 14:12:59 +02:00

293 lines
No EOL
7.5 KiB
Markdown

SELECT statement is used to retrieve records from one or more tables in PostgreSQL.
More about SELECT statement in postgres can be found at: https://www.postgresql.org/docs/11/sql-select.html
Following clauses are supported:
- SELECT(expressions...) - expressions to form output rows of the SELECT statement.
- DISTINCT() - remove all duplicate rows from result set
- FROM(tableSource...) - specifies one or more source tables for the SELECT.
- WHERE(condition) - only rows for which condition returns true will be selected.
- GROUP BY(groupingElement, ...) - will condense into a single row all selected rows that share the same values for the grouped expressions.
- HAVING(condition) - eliminates group rows that do not satisfy the condition
- ORDER BY(orderBy, ...) - causes the result rows to be sorted according to the specified expression(s)
- LIMIT(count) - specifies the maximum number of rows to return
- OFFSET(start) - specifies the number of rows to skip before starting to return rows
- FOR(lockMode) - how SELECT will lock rows as they are obtained from the table
Lock mode can be:
- jet.UPDATE()
- jet.NO_KEY_UPDATE()
- jet.SHARE()
- jet.KEY_SHARE()
Lock mode can be extended with following clauses:
- NOWAIT()
- SKIP_LOCKED()
- UNION(select) / UNION_ALL(select) - computes the set union of the rows returned by the involved SELECT statements
- INTERSECT(select) / INTERSECT_ALL(select) - computes the set intersection of the rows returned by the involved SELECT statements
- EXCEPT(select) / EXCEPT_ALL(select) - computes the set of rows that are in the result of the left SELECT statement but not in the result of the right one
_This list might be extended with feature Jet releases._
### Examples per clause
##### 1. SELECT clause
Sample SELECT clause written in Go:
```
jet.SELECT(
jet.Int(1).ADD(jet.Int(12)).SUB(jet.Int(21)), // arbitrary expression
table.Film.Name, // column
table.Customer.FirstName.CONCAT(table.Customer.LastName).AS("FullName") // alias
)
```
If jet and autogenerated table package, is dot "." imported, above statement will look more like native SQL:
```
SELECT(
Int(1).ADD(Int(12)).SUB(Int(21)), // arbitrary expression
Film.Name, // column
Customer.FirstName.CONCAT(Customer.LastName).AS("FullName") // alias
)
```
*For the most of the wiki dot import is used.*
Above SQL clause written in go will produce following raw SQL:
```
SELECT 1 + 12 - 21,
film.name AS "film.name", --
customer.first_name || customer.last_name AS "FullName"
```
`film.name AS "film.name"` - column names are aliased by default. Alias is used during execution to map row result to
appropriate `model` structure.
##### 2. DISTINCT clause
```
SELECT(Film.Name).
.DISTINCT().
```
Raw SQL:
```
SELECT DISTINCT film.name
```
##### 3. FROM clause
```
Go:
1).FROM(Film)
2).FROM(
Film.
INNER_JOIN(Language, Langauge.LanguageID.EQ(Film.FilmID))
)
SQL:
1)FROM dvds.film
2)FROM Film
INNER JOIN Language ON (Language.LanguageID = Film.FilmID)
```
##### 4. WHERE clause
```
Go:
.WHERE(Film.Length.GT(Int(150)))
SQL:
WHERE film.length > 150
```
##### 5. GROUP BY clause
```
Go:
.GROUP_BY(Film.Length)
SQL:
GROUP BY film.length
```
##### 6. HAVING clause
```
Go:
.HAVING(Film.Length.GT(Int(150))
SQL:
HAVING film.length > 150
```
##### 7. ORDER BY clause
```
Go:
.ORDER_BY(Film.Length)
SQL:
ORDER BY film.length
```
##### 8. LIMIT clause
```
Go:
.LIMIT(11)
SQL:
LIMIT 11
```
##### 9. OFFSET clause
```
Go:
.OFFSET(11)
SQL:
OFFSET 11
```
##### 9. FOR clause
```
Go:
.FOR(jet.NO_KEY_UPDATE().SKIP_LOCKED())
SQL:
FOR NO KEY UPDATE SKIP LOCKED
```
##### 10. Set clauses (UNION, UNION_ALL, INTERSECT, INTERSECT_ALL, EXCEPT, EXCEPT_ALL)
```
Go:
SELECT(Payment.Amount).FROM(Payment)
UNION_ALL(SELECT(Payment.Amount).FROM(Payment))
Sql:
(
(
SELECT payment.amount AS "payment.amount"
FROM dvds.payment
)
UNION
(
SELECT payment.amount AS "payment.amount"
FROM dvds.payment
)
);
```
### Two forms of select statements in Jet
#### 1. Classical select statement
Columns selected are before table source selected
```
SELECT(
Payment.AllColumns,
Customer.AllColumns,
).
FROM(Payment.
INNER_JOIN(Customer, Payment.CustomerID.EQ(Customer.CustomerID))).
ORDER_BY(Payment.PaymentID.ASC()).
LIMIT(30)
```
#### 2. Jet select statement
Table sources are before columns selected. There is no FROM.
```
Payment.
INNER_JOIN(Customer, Payment.CustomerID.EQ(Customer.CustomerID))
SELECT(
Payment.AllColumns,
Customer.AllColumns,
).
ORDER_BY(Payment.PaymentID.ASC()).
LIMIT(30)
```
Second form is added, because sometimes feels more natural to first think about the tables
of interest, and than about the columns.
**Both forms produces exactly the same raw SQL.**
## Sub-queries
How to write SELECT statement with sub-queries?
Sub-queries are composed first:
```
// select film_id and title from film table that have 'R' rating.
rRatingFilms := Film.
SELECT(
Film.FilmID,
Film.Title,
).
WHERE(Film.Rating.EQ(enum.MpaaRating.R)).
AsTable("rFilms")
```
`AsTable("rFilms")` - allows SELECT statements to be seen as source table.
To use sub-query columns in 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 := Actor.
INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.FilmID)).
INNER_JOIN(rRatingFilms, FilmActor.FilmID.EQ(rFilmId)).
SELECT(
Actor.AllColumns,
FilmActor.AllColumns,
rRatingFilms.AllColumns(),
)
```
`rRatingFilms.AllColumns(),` - sub-query columns needed for projection can be exported with AllColumns() method.
No need to export each one of them with `From`.
Debug SQL of above example:
```sql
SELECT 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",
film_actor.actor_id AS "film_actor.actor_id",
film_actor.film_id AS "film_actor.film_id",
film_actor.last_update AS "film_actor.last_update",
"rFilms"."film.film_id" AS "film.film_id",
"rFilms"."film.title" AS "film.title",
"rFilms"."film.rating" AS "film.rating"
FROM dvds.actor
INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.film_id)
INNER JOIN (
SELECT film.film_id AS "film.film_id",
film.title AS "film.title",
film.rating AS "film.rating"
FROM dvds.film
WHERE film.rating = 'R'
) AS "rFilms" ON (film_actor.film_id = "rFilms"."film.film_id");
```
#### What If sub-query projection is not a column?
For instance:
```
customersPayments := Payment.
SELECT(
Payment.CustomerID,
SUMf(Payment.Amount).AS("amount_sum"),
).
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.
Because SUMf produces float expression we will create FloatColumn with name of the column alias `"amount_sum"`
```
amountSum := FloatColumn("amount_sum").From(customersPayments)
```