In this answer I will tell more than just the choice and the question itself!
selection and prevention of name conflicts
The principle of choice is to use select . And name conflicts can be avoided by renaming columns (aliases). And to create aliases (renaming a column), simply use as in the column name row, as shown below:
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 as shown below:
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' );
Examples of multiple unions (3 or more)
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' );
Note how to use the as() method!
additional (not related to the choice, but creates and combines)
Filtering with:
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');
Same as above, without function construction (and with several 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______/ );