I have worked about 20 years with SQL databases and seem to have trouble understanding Knex way to map queries. Can someone help me get the correct code?
I have this SQL query that I want to use in my nodejs application:
SELECT p.id, p.project_number, p.project_name, p.start_date, p.end_date,
o.name, o.email_addr,
c.company, c.email_addr AS company_email, c.first_name, c.last_name
FROM projects p
INNER JOIN owners o ON o.id = p.owner_id
INNER JOIN clients c ON c.id = p.client_id
KnexJS (0.7.5) documentation shows example that would look like this for my query:
knex.from('projects').innerJoin('owners', 'owners.id', 'projects.owner_id')
.innerJoin('clients', 'clients.id', 'projects.client_id');
There are couple of things I cannot find in the documentation:
1) how do I select which columns I want to include? Projects, Clients and Owners each have 20 to 50 columns and I am not interested in all of them. Selecting columns from the main table is clear (using select() or column() ) but how to select columns from the joined tables?
2) Some columns have identical names. How can I avoid name conflicts (i.e. add some prefix to columns from other tables)? I looked at the way Knex can generate column aliases (... AS ...) and I am not sure it is a viable option for more complex queries. (Even for relatively simple queries like the one above)
You can try:
knex('projects').select(['projects.id', 'projects.project_number', 'projects.project_name', 'projects.start_date', 'projects.end_date',
'owners.name', 'owners.email_addr as owner_email_addr',
'clients.company', 'clients.email_addr as client_email_addr', 'clients.first_name', 'clients.last_name'])
.innerJoin('owners', 'owners.id', 'projects.owner_id')
.innerJoin('clients', 'clients.id', 'projects.client_id');
Hope this helps!
In this answer i will cover more then just selecting and the question itself !
The principle to select would be to use select
. And name conflicts can be avoided by renaming the columns (aliasing).
And to create aliases (column renaming) just use as
within the column name string as in the snippet bellow:
knex
.select([
'table_a.id',
'table_b.id as b_id' // ---> the way to create aliases (to avoid
]) // columns overlapping and overriding
.from('table_a')
.leftJoin(
'table_b',
'table_a.table_b_id',
'table_b.id'
);
It will produce this SQL:
SELECT
"table_a"."id",
"table_b"."id" as "b_id"
FROM
"table_a"
LEFT JOIN "table_b" ON
"table_a"."table_b_id" = "table_b"."id"
We can formulate in different ways. Some interesting ones are as in bellow:
knex('processes') // <-- wihtout using from (shorter) [less sql like though]
.select([
'processes.id as processId',
'processes.name as processName',
'processes.description as processDescription',
'processes.deleted as processDeleted',
'processes.deleteTime as processDeleteTime',
'rsp.runningSettingId',
'rsp.value as settingValue',
'rsp.startTime as settingStartTime'
])
.innerJoin(
'runningSettings_processes as rsp',
'processes.id',
'rsp.processId'
);
const superJoinData = await knex( // i'm using async await for the promises
knex<Process>('process')//__________________________from here
.select([
'processes.id as processId',
'processes.name as processName', //--- conflict with rs.name
'processes.description as processDescription', //-- conflict with rs.description
'processes.deleted as processDeleted',
'processes.deleteTime as processDeleteTime',
'rsp.runningSettingId',
'rsp.value as settingValue',
'rsp.startTime as settingStartTime'
])
.innerJoin(
'runningSettings_processes as rsp',
'processes.id',
'rsp.processId'
)//_______________________to here
.as('prsp') // |first join. (result an equivalent of a table)
) // !!!!!!!!!!!!!!!!! notice how we nested a knex construct within another!!
.select([ // select from the resulting table of the first join !!!!
'prsp.processId',
'prsp.processName',
'prsp.processDescription',
'prsp.processDeleted',
'prsp.processDeleteTime',
'prsp.runningSettingId',
'prsp.settingValue',
'prsp.settingStartTime',
'rs.name as settingName',
'rs.description as settingDescription'
])
.innerJoin( // ______________second inner join
'runningSettings as rs',
'prsp.runningSettingId',
'rs.id'
);
Notice how to use
as()
method!
Filtering with where:
knex(
knex('A').where('A.id',1).as('t1')
).leftJoin(
knex('B').where('B.id', 2).as('t2'),
't1.c',
't2.d'
)
//or filter the resulting table of the join
knex('A')
.leftJoin(
knex('B').where('B.id', 2).as('t2'),
't1.c',
't2.d'
)
.where('someColumn', 2);
Using callbacks for further control
knex
.select('*')
.from(function () {
this.select('*').from('A')
.where('id',1)
.as('t1');
})
.leftJoin(
knex('B').where('id',2).as('t2'),
function () {
this.on('t1.c', '=', 't2.d'); // <--- notice also the use of function
} // construct for the join condition
);
Same as above without function construct (and with multiple join conditions as a bonus)
knex( // <--- notice the knex constructor (equivTo: knex.select().from())
knex('A') //NT: we already used that above when answering the question
.select('*')
.where('id',1)
.as('t1');
)
.leftJoin(
knex('B').where('id',2).as('t2'),
function () { // <----- notice that in place of using the 2nd and 3d arg, we can have a function in the 2nd arg
this.on('t1.c', '=', 't2.d').on('t1.s', '=', 't2.g'); // <-----|
} // plus: multiple on conditions______/
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With