Select
Select with joins and order by
const firstName = 'John';
const lastName = null;
const company = tCompany.as('comp');
const customersWithCompanyName = connection.selectFrom(tCustomer)
.innerJoin(company).on(tCustomer.companyId.equals(company.id))
.where(tCustomer.firstName.startsWithInsensitive(firstName))
.and(tCustomer.lastName.startsWithInsensitiveIfValue(lastName))
.select({
id: tCustomer.id,
firstName: tCustomer.firstName,
lastName: tCustomer.lastName,
birthday: tCustomer.birthday,
companyName: company.name
})
.orderBy('firstName', 'insensitive')
.orderBy('lastName', 'asc insensitive')
.executeSelectMany();
The executed query is:
select customer.id as id, customer.first_name as firstName, customer.last_name as lastName, customer.birthday as birthday, comp.name as companyName
from customer inner join company as comp on customer.company_id = comp.id
where customer.first_name ilike ($1 || '%')
order by lower(firstName), lower(lastName) asc
The parameters are: [ 'John' ]
The result type is:
const customersWithCompanyName: Promise<{
id: number;
firstName: string;
lastName: string;
companyName: string;
birthday?: Date;
}[]>
The name of the columns to order corresponds to the name/path in the query's result. The supported order by modes are:
type OrderByMode = 'asc' | 'desc' | 'asc nulls first' | 'asc nulls last' | 'desc nulls first' | 'desc nulls last' | 'insensitive' |
'asc insensitive' | 'desc insensitive' | 'asc nulls first insensitive' | 'asc nulls last insensitive' |
'desc nulls first insensitive' | 'desc nulls last insensitive'
For the databases that don't support null first
or null last
, a proper order by that emulates that behaviour is generated. The insensitive
modifier makes the ordering key-insensitive according to the insensitive strategy defined in your connection. In case the insensitive
modifier is used in a not string column, the modifier will be just ignored.
You can project optional values in objects as always-required properties that allow null calling projectingOptionalValuesAsNullable()
immediately after select(...)
.
Select ordering by a not returned column
const customerId = 10;
const customerWithId = connection.selectFrom(tCustomer)
.where(tCustomer.id.equals(customerId))
.select({
id: tCustomer.id,
firstName: tCustomer.firstName,
lastName: tCustomer.lastName
})
.orderBy(tCustomer.birthday, 'desc nulls last')
.executeSelectOne();
The executed query is:
select id as id, first_name as firstName, last_name as lastName
from customer
where id = $1
order by customer.birthday desc nulls last
The parameters are: [ 10 ]
The result type is:
const customerWithId: Promise<{
id: number;
firstName: string;
lastName: string;
}>
Select with subquery and dynamic order by
const orderBy = 'customerFirstName asc nulls first, customerLastName';
const customerWithSelectedCompanies = connection.selectFrom(tCustomer)
.where(tCustomer.companyId.in(
connection.selectFrom(tCompany)
.where(tCompany.name.contains('Cia.'))
.selectOneColumn(tCompany.id)
)).select({
customerId: tCustomer.id,
customerFirstName: tCustomer.firstName,
customerLastName: tCustomer.lastName
}).orderByFromString(orderBy)
.executeSelectMany();
The executed query is:
select id as customerId, first_name as customerFirstName, last_name as customerLastName
from customer
where company_id in (
select id as result from company where name like ('%' || $1 || '%')
)
order by customerFirstName asc nulls first, customerLastName
The parameters are: [ 'Cia.' ]
The result type is:
const customerWithSelectedCompanies: Promise<{
customerId: number;
customerFirstName: string;
customerLastName: string;
}[]>
Select with aggregate functions and group by
const customerCountPerCompany = connection.selectFrom(tCompany)
.innerJoin(tCustomer).on(tCustomer.companyId.equals(tCompany.id))
.groupBy(tCompany.id, tCompany.name)
.select({
companyId: tCompany.id,
companyName: tCompany.name,
customerCount: connection.count(tCustomer.id)
})
.executeSelectMany();
The executed query is:
select company.id as companyId, company.name as companyName, count(customer.id) as customerCount
from company inner join customer on customer.company_id = company.id
group by company.id, company.name
The parameters are: []
The result type is:
const customerCountPerCompany: Promise<{
companyId: number;
companyName: string;
customerCount: number;
}[]>
Select with left join
To use a table or view on a left join, you must get a left join representation calling the method forUseInLeftJoin
or forUseInLeftJoinAs
previous to write the query.
const parent = tCompany.forUseInLeftJoinAs('parent');
const leftJoinCompany = connection.selectFrom(tCompany)
.leftJoin(parent).on(tCompany.parentId.equals(parent.id))
.select({
id: tCompany.id,
name: tCompany.name,
parentId: parent.id,
parentName: parent.name
}).executeSelectMany();
The executed query is:
select company.id as id, company.name as name, parent.id as parentId, parent.name as parentName
from company left join company as parent on company.parent_id = parent.id
The parameters are: []
The result type is:
const leftJoinCompany: Promise<{
id: number;
name: string;
parentId?: number;
parentName?: string;
}[]>
When you are doing a left join, you probably want to use Complex projections:
const parent = tCompany.forUseInLeftJoinAs('parent');
const leftJoinCompany = await connection.selectFrom(tCompany)
.leftJoin(parent).on(tCompany.parentId.equals(parent.id))
.select({
id: tCompany.id,
name: tCompany.name,
parent: {
id: parent.id,
name: parent.name
}
}).executeSelectMany();
The executed query is:
select company.id as id, company.name as name,
parent.id as "parent.id", parent.name as "parent.name"
from company left join company as parent on company.parent_id = parent.id
The parameters are: []
The result type is:
const leftJoinCompany: Promise<{
id: number;
name: string;
parent?: {
id: number;
name: string;
};
}[]>
Select with a compound operator (union, intersect, except)
const allDataWithName = connection.selectFrom(tCustomer)
.select({
id: tCustomer.id,
name: tCustomer.firstName.concat(' ').concat(tCustomer.lastName),
type: connection.const<'customer' | 'company'>('customer', 'enum', 'customerOrCompany')
}).unionAll(
connection.selectFrom(tCompany)
.select({
id: tCompany.id,
name: tCompany.name,
type: connection.const<'customer' | 'company'>('company', 'enum', 'customerOrCompany')
})
).executeSelectMany();
The executed query is:
select id as id, first_name || $1 || last_name as name, $2 as type
from customer
union all
select id as id, name as name, $3 as type
from company
The parameters are: [ ' ', 'customer', 'company' ]
The result type is:
const allDataWithName: Promise<{
id: number;
name: string;
type: "customer" | "company";
}[]>
Note: depending on your database, the supported compound operators are: union
, unionAll
, intersect
, intersectAll
, except
, exceptAll
, minus
(alias for except
), minusAll
(alias for exceptAll
)
Using a select as a view in another select query (SQL with clause)
You can define a select query and use it as it were a view in another select query. To allow ait you must call the forUseInQueryAs
instead of executing the query; this will return a view representation of the query as it were a view, and the query will be included as a with
clause in the final sql query with the name indicated by argument to the forUseInQueryAs
method.
const customerCountPerCompanyWith = connection.selectFrom(tCompany)
.innerJoin(tCustomer).on(tCustomer.companyId.equals(tCompany.id))
.select({
companyId: tCompany.id,
companyName: tCompany.name,
customerCount: connection.count(tCustomer.id)
}).groupBy('companyId', 'companyName')
.forUseInQueryAs('customerCountPerCompany');
const customerCountPerAcmeCompanies = connection.selectFrom(customerCountPerCompanyWith)
.where(customerCountPerCompanyWith.companyName.containsInsensitive('ACME'))
.select({
acmeCompanyId: customerCountPerCompanyWith.companyId,
acmeCompanyName: customerCountPerCompanyWith.companyName,
acmeCustomerCount: customerCountPerCompanyWith.customerCount
})
.executeSelectMany();
The executed query is:
with
customerCountPerCompany as (
select company.id as companyId, company.name as companyName, count(customer.id) as customerCount
from company inner join customer on customer.company_id = company.id
group by company.id, company.name
)
select companyId as "acmeCompanyId", companyName as "acmeCompanyName", customerCount as "acmeCustomerCount"
from customerCountPerCompany
where companyName ilike ('%' || $1 || '%')
The parameters are: [ 'ACME' ]
The result type is:
const customerCountPerAcmeCompanies: Promise<{
acmeCompanyId: number;
acmeCompanyName: string;
acmeCustomerCount: number;
}[]>
Select count all
const companyId = 10;
const numberOfCustomers = connection.selectFrom(tCustomer)
.where(tCustomer.companyId.equals(companyId))
.selectCountAll() // Shortcut to .selectOneColumn(connection.countAll())
.executeSelectOne();
The executed query is:
select count(*) as result
from customer
where company_id = $1
The parameters are: [ 10 ]
The result type is:
const numberOfCustomers: Promise<number>
Inline select as value for another query
To use a select query that returns one column as an inline query value, you must get the value representation by calling the method forUseAsInlineQueryValue
and then use the value representation as with any other value in a secondary query.
const acmeId = connection.selectFrom(tCompany)
.where(tCompany.name.equals('ACME'))
.selectOneColumn(tCompany.id)
.forUseAsInlineQueryValue();
const acmeCustomers = connection.selectFrom(tCustomer)
.where(tCustomer.companyId.equals(acmeId))
.select({
id: tCustomer.id,
name: tCustomer.firstName.concat(' ').concat(tCustomer.lastName)
})
.executeSelectMany();
The executed query is:
select id as id, first_name || $1 || last_name as name
from customer
where company_id = (select id as result from company where name = $2)
The parameters are: [ ' ', 'ACME' ]
The result type is:
const acmeCustomers: Promise<{
name: string;
id: number;
}[]>
Inline select as value for another query referencing the outer query
To use a select query that returns one column as an inline query value that references the outer query's tables, you must start the subquery calling subSelectUsing
and providing by argument the external tables or views required to execute the subquery, and then get the value representation, in the end, by calling the method forUseAsInlineQueryValue
and then use the value representation as with any other value in the outer query.
const numberOfCustomers = connection
.subSelectUsing(tCompany)
.from(tCustomer)
.where(tCustomer.companyId.equals(tCompany.id))
.selectOneColumn(connection.countAll())
.forUseAsInlineQueryValue() // At this point is a value that you can use in other query
.valueWhenNull(0);
const companiesWithNumberOfCustomers = await connection.selectFrom(tCompany)
.select({
id: tCompany.id,
name: tCompany.name,
numberOfCustomers: numberOfCustomers
})
.executeSelectMany();
The executed query is:
select
id as id,
name as name,
coalesce((
select count(*) as result
from customer
where company_id = company.id
), $1) as numberOfCustomers
from company
The parameters are: [ 0 ]
The result type is:
const companiesWithNumberOfCustomers: Promise<{
id: number;
name: string;
numberOfCustomers: number;
}[]>
In the previous example, it will be more convenient to use .selectCountAll()
instead .selectOneColumn(connection.countAll())
because the returned value will not be optional; then, you will not need to use .valueWhenNull(0)
.
const numberOfCustomers = connection
.subSelectUsing(tCompany)
.from(tCustomer)
.where(tCustomer.companyId.equals(tCompany.id))
.selectCountAll()
.forUseAsInlineQueryValue(); // At this point is a value that you can use in other query
const companiesWithNumberOfCustomers = connection.selectFrom(tCompany)
.select({
id: tCompany.id,
name: tCompany.name,
numberOfCustomers: numberOfCustomers
})
.executeSelectMany();
The executed query is:
select
id as id,
name as name,
(
select count(*) as result
from customer
where company_id = company.id
) as numberOfCustomers
from company
The parameters are: [ ]
The result type is:
const companiesWithNumberOfCustomers: Promise<{
id: number;
name: string;
numberOfCustomers: number;
}[]>
Select clauses order
The select query clauses must follow one of the next orders:
- Logical order: from, join, WHERE, group by, having, select, order by, limit, offset, customizeQuery, compose/split
- Alternative logical order 1: from, join, group by, having, WHERE, select, order by, limit, offset, customizeQuery, compose/split
- Arternative logical order 2: from, join, group by, having, select, WHERE, order by, limit, offset, customizeQuery, compose/split
- Arternative logical order 3: from, join, group by, having, select, order by, WHERE, limit, offset, customizeQuery, compose/split
- Arternative logical order 4: from, join, group by, having, select, order by, limit, offset, WHERE, customizeQuery, compose/split
- Arternative logical order 5: from, join, group by, having, select, order by, limit, offset, customizeQuery, WHERE, compose/split
-
Arternative logical order 6: from, join, group by, having, select, order by, limit, offset, customizeQuery, compose/split, WHERE
-
Alternative order 1: from, join, select, WHERE, group by, having, order by, limit, offset, customizeQuery, compose/split
- Alternative order 2: from, join, select, group by, having, WHERE, order by, limit, offset, customizeQuery, compose/split
- Alternative order 3: from, join, select, group by, having, order by, WHERE, limit, offset, customizeQuery, compose/split
- Alternative order 4: from, join, select, group by, having, order by, limit, offset, WHERE, customizeQuery, compose/split
- Alternative order 5: from, join, select, group by, having, order by, limit, offset, customizeQuery, WHERE, compose/split
- Alternative order 6: from, join, select, group by, having, order by, limit, offset, customizeQuery, compose/split, WHERE
Oracle variants:
Oracle support start with, connect by and ordering siblings only. The ordering siblings only modifier changes the previous order by
to order siblings by
.
- Logical order: from, join, start with, connect by, WHERE, group by, having, select, order by, ordering siblings only, limit, offset, customizeQuery, compose/split
- Alternative logical order 0: from, join, WHERE, start with, connect by, group by, having, select, order by, ordering siblings only, limit, offset, customizeQuery, compose/split
- Alternative logical order 1: from, join, start with, connect by, group by, having, WHERE, select, order by, ordering siblings only, limit, offset, customizeQuery, compose/split
- Arternative logical order 2: from, join, start with, connect by, group by, having, select, WHERE, order by, ordering siblings only, limit, offset, customizeQuery, compose/split
- Arternative logical order 3: from, join, start with, connect by, group by, having, select, order by, ordering siblings only, WHERE, limit, offset, customizeQuery, compose/split
- Arternative logical order 4: from, join, start with, connect by, group by, having, select, order by, ordering siblings only, limit, offset, WHERE, customizeQuery, compose/split
- Arternative logical order 5: from, join, start with, connect by, group by, having, select, order by, ordering siblings only, limit, offset, customizeQuery, WHERE, compose/split
-
Arternative logical order 6: from, join, start with, connect by, group by, having, select, order by, ordering siblings only, limit, offset, customizeQuery, compose/split, WHERE
-
Alternative order 1: from, join, start with, connect by, select, WHERE, group by, having, order by, ordering siblings only, limit, offset, customizeQuery, compose/split
- Alternative order 2: from, join, start with, connect by, select, group by, having, WHERE, order by, ordering siblings only, limit, offset, customizeQuery, compose/split
- Alternative order 3: from, join, start with, connect by, select, group by, having, order by, ordering siblings only, WHERE, limit, offset, customizeQuery, compose/split
- Alternative order 4: from, join, start with, connect by, select, group by, having, order by, ordering siblings only, limit, offset, WHERE, customizeQuery, compose/split
- Alternative order 5: from, join, start with, connect by, select, group by, having, order by, ordering siblings only, limit, offset, customizeQuery, WHERE, compose/split
- Alternative order 6: from, join, start with, connect by, select, group by, having, order by, ordering siblings only, limit, offset, customizeQuery, compose/split, WHERE
-
Logical order (Oracle variant): from, join, WHERE, start with, connect by, group by, having, select, order by, ordering siblings only, limit, offset, customizeQuery, compose/split
-
Second alternative order 0: from, join, select, WHERE, start with, connect by, group by, having, order by, ordering siblings only, limit, offset, customizeQuery, compose/split
- Second alternative order 1: from, join, select, start with, connect by, WHERE, group by, having, order by, ordering siblings only, limit, offset, customizeQuery, compose/split
- Second alternative order 2: from, join, select, start with, connect by, group by, having, WHERE, order by, ordering siblings only, limit, offset, customizeQuery, compose/split
- Second alternative order 3: from, join, select, start with, connect by, group by, having, order by, ordering siblings only, WHERE, limit, offset, customizeQuery, compose/split
- Second alternative order 4: from, join, select, start with, connect by, group by, having, order by, ordering siblings only, limit, offset, WHERE, customizeQuery, compose/split
- Second alternative order 5: from, join, select, start with, connect by, group by, having, order by, ordering siblings only, limit, offset, customizeQuery, WHERE, compose/split
- Second alternative order 6: from, join, select, start with, connect by, group by, having, order by, ordering siblings only, limit, offset, customizeQuery, compose/split, WHERE