
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)
```