SELECT (original) (raw)

SELECT statement is used to retrieve records from one or more tables in PostgreSQL.
More about SELECT statement can be found at:
PostgreSQL - https://www.postgresql.org/docs/11/sql-select.html
MySQL - https://dev.mysql.com/doc/refman/8.0/en/select.html
MariaDB - https://mariadb.com/kb/en/library/select/


Contents

Supported clauses

This list might be extended with feature Jet releases.

Example per clause

SELECT clause

Sample SELECT clause written in Go:

// dot "." import implied SELECT( Int(1).ADD(Int(12)).SUB(Int(21)), // arbitrary expression Film.Name, // column Customer.FirstName.CONCAT(Customer.LastName).AS("FullName") // alias )

Above SQL clause 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.

OPTIMIZER_HINTS (MySQL only)

SELECT(Actor.ActorID). OPTIMIZER_HINTS(MAX_EXECUTION_TIME(1), QB_NAME("mainQueryBlock"), "NO_ICP(actor)")

SELECT /*+ MAX_EXECUTION_TIME(1) QB_NAME(mainQueryBlock) NO_ICP(actor) */ actor.actor_id AS "actor.actor_id"

DISTINCT clause

// Go:

SELECT(Film.Name). DISTINCT().

// PostgreSQL only SELECT(Film.Duration, Film.Rating, Film.Name). DISTINCT(Film.Duration, Film.Rating)

-- SQL:

SELECT DISTINCT film.name AS "film.name"

// PostgreSQL only SELECT DISTINCT ON (film.duration, film.rating) film.duration AS "film.duration" film.rating AS "film.rating" film.name AS "film.name"

FROM clause

The FROM clause specifies one or more source tables for the SELECT.

Go:

  1. .FROM(Film)
  2. .FROM( Film. INNER_JOIN(Language, Langauge.LanguageID.EQ(Film.FilmID)) )
  3. .FROM(Film, Language, Artist) // implicit CROSS JOIN

SQL:

  1. FROM dvds.film
  2. FROM Film INNER JOIN Language ON (Language.LanguageID = Film.FilmID)
  3. FROM Film, Language, Artists

WHERE clause

Go: .WHERE(Film.Length.GT(Int(150)))

SQL: WHERE film.length > 150

GROUP BY clause

Go:

  1. .GROUP_BY(Film.Length) // postgres
  2. .GROUP_BY( GROUPING_SETS( WRAP(Inventory.FilmID, Inventory.StoreID), WRAP(Inventory.FilmID), WRAP(), ), )
  3. .GROUP_BY( CUBE(Country.Country, City.City), )
  4. .GROUP_BY( ROLLUP(Country.Country, City.City), ) // mysql
  5. .GROUP_BY( WITH_ROLLUP(Inventory.FilmID, Inventory.StoreID), )

SQL:

  1. GROUP BY film.length -- postgres
  2. GROUP BY GROUPING SETS((inventory.film_id, inventory.store_id), (inventory.film_id), ())
  3. GROUP BY CUBE(country.country, city.city)
  4. GROUP BY ROLLUP(country.country, city.city) -- mysql
  5. GROUP BY inventory.film_id, inventory.store_id WITH ROLLUP

WINDOW

SELECT( AVG(Payment.Amount).OVER(), MINf(Payment.Amount).OVER(PARTITION_BY(Payment.CustomerID).ORDER_BY(Payment.PaymentDate.DESC())), ROW_NUMBER().OVER(Window("w1")), RANK().OVER( Window("w2"). ORDER_BY(Payment.CustomerID). RANGE(PRECEDING(UNBOUNDED), FOLLOWING(UNBOUNDED)), ), AVG(Payment.Amount).OVER(Window("w3").ROWS(PRECEDING(1), FOLLOWING(2))), ). FROM(Payment). WINDOW("w1").AS(PARTITION_BY(Payment.PaymentDate)). WINDOW("w2").AS(Window("w1")). WINDOW("w3").AS(Window("w2").ORDER_BY(Payment.CustomerID)).

SELECT AVG(payment.amount) OVER (), MIN(payment.amount) OVER (PARTITION BY payment.customer_id ORDER BY payment.payment_date DESC), ROW_NUMBER() OVER (w1), RANK() OVER (w2 ORDER BY payment.customer_id RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), AVG(payment.amount) OVER (w3 ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING) FROM dvds.payment WINDOW w1 AS (PARTITION BY payment.payment_date), w2 AS (w1), w3 AS (w2 ORDER BY payment.customer_id);

HAVING clause

Go: .HAVING(SUMi(Film.Length).GT(Int(150))

SQL: HAVING SUM(film.length) > 150

ORDER BY clause

Go: .ORDER_BY(Film.Length)

SQL: ORDER BY film.length

LIMIT clause

OFFSET clause

FOR clause

Go: .FOR(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

1. Classical select statement

Columns selected are before table sources(FROM clause)

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 clause.

Payment. INNER_JOIN(Customer, Payment.CustomerID.EQ(Customer.CustomerID)) SELECT( Payment.AllColumns, Customer.AllColumns, ). ORDER_BY(Payment.PaymentID.ASC()). LIMIT(30)

Note

Jet form is added, because sometimes feels more natural to first think about the tables of interest, and then about the columns. Although the second form feels more natural, the first form is preferred because it looks and feels more like a native SQL.
Both forms produce exactly the same raw SQL.

Table aliasing

// alias first manager := Employee.AS("Manager")

// then aliased table can be used in a statement stmt := SELECT( manager.AllColumns, ).FROM( manager, )

SELECT "Manager"."EmployeeId" AS "Manager.EmployeeId", "Manager"."LastName" AS "Manager.LastName", "Manager"."FirstName" AS "Manager.FirstName", "Manager"."Title" AS "Manager.Title", FROM chinook."Employee" AS "Manager";

Note that model.Employee can not be used as a destination for this query. Expected destination type name is now Manager.
To use generated model types with aliased tables there are two options:

  1. Define new type

type Manager model.Employee var dest Manager

err := stmt.Query(db, &dest)

  1. Field aliasing

var dest struct { Manager model.Employee alias:"Manager.*" }

err := stmt.Query(db, &dest)