有时您可能需要在查询中使用原始表达式。原始查询对象可能会注入到几乎您想要的任何位置,并且使用适当的绑定可以确保正确地转义您的值,从而防止SQL注入攻击。


原始参数绑定:

可以将给的sql参数化knex.raw(sql, bindings)。参数可以在位置上命名。也可以选择将参数视为值还是sql标识符,例如在'TableName.ColumnName'引用的情况下。


knex('users')

  .select(knex.raw('count(*) as user_count, status'))

  .where(knex.raw(1))

  .orWhere(knex.raw('status <> ?', [1]))

  .groupBy('status')

输出:

select count(*) as user_count, status from `users` where 1 or status <> 1 group by `status`

位置绑定?被解释为值,并被??解释为标识符。


knex('users').where(knex.raw('?? = ?', ['user.name', 1]))

输出:

select * from `users` where `user`.`name` = 1

诸如这样的命名绑定:name被解释为值,并且被:name:解释为标识符。只要值不是,就处理命名的绑定undefined。


knex('users')

  .where(knex.raw(':name: = :thisGuy or :name: = :otherGuy or :name: = :undefinedBinding', {

    name: 'users.name',

    thisGuy: 'Bob',

    otherGuy: 'Jay',

    undefinedBinding: undefined

  }))

错误:

Undefined binding(s) detected for keys [undefinedBinding] when compiling RAW query: `users`.`name` = ? or `users`.`name` = ? or `users`.`name` = :undefinedBinding

对于一个只有一个绑定的简单查询,.raw可以接受该绑定作为第二个参数。


knex('users')

  .where(

    knex.raw('LOWER("login") = ?', 'knex')

  )

  .orWhere(

    knex.raw('accesslevel = ?', 1)

  )

  .orWhere(

    knex.raw('updtime = ?', new Date.UTC('01-01-2016'))

  )

错误:

Date.UTC is not a constructor

由于数组绑定没有统一的语法,因此您需要通过?直接在查询中添加将它们视为多个值。


const myArray = [1,2,3]

knex.raw('select * from users where id in (' + myArray.map(_ => '?').join(',') + ')', [...myArray]);

// query will become: select * from users where id in (?, ?, ?) with bindings [1,2,3]

错误:

Unexpected token const

为了防止替换?一个可以使用转义序列\\?。


knex.select('*').from('users').where('id', '=', 1).whereRaw('?? \\? ?', ['jsonColumn', 'jsonKey'])

输出:

select * from `users` where `id` = 1 and `jsonColumn` ? 'jsonKey'

为了防止替换命名的绑定,可以使用转义序列\\:。


knex.select('*').from('users').whereRaw(":property: = '\\:value' OR \\:property: = :value", {

  property: 'name',

  value: 'Bob'

})

输出:

select * from `users` where `name` = ':value' OR :property: = 'Bob'

原始表达式:

通过使用原始表达式knex.raw(sql, [bindings])并将其作为查询链中任何值的值来创建。


knex('users')

  .select(knex.raw('count(*) as user_count, status'))

  .where(knex.raw(1))

  .orWhere(knex.raw('status <> ?', [1]))

  .groupBy('status')

输出:

select count(*) as user_count, status from `users` where 1 or status <> 1 group by `status`

原始查询:

该knex.raw也可用于构建一个完整的查询并执行它,作为一个标准的查询生成器的查询将被执行。这样做的好处是,它使用连接池并为不同的客户端库提供标准接口。


knex.raw('select * from users where id = ?', [1]).then(function(resp) { ... });

请注意,响应将是底层sql库通常在正常查询中返回的结果,因此您可能需要查看查询所针对的基础库的文档,以确定如何处理响应。


打包查询:

原始查询构建器还附带一种wrap方法,该方法允许将查询包装在一个值中:


var subcolumn = knex.raw('select avg(salary) from employee where dept_no = e.dept_no')

.wrap('(', ') avg_sal_dept');


knex.select('e.lastname', 'e.salary', subcolumn)

.from('employee as e')

.whereRaw('dept_no = e.dept_no')

输出:

select `e`.`lastname`, `e`.`salary`, (select avg(salary) from employee where dept_no = e.dept_no) avg_sal_dept from `employee` as `e` where dept_no = e.dept_no

注意,使用as方法更容易实现上述示例。


var subcolumn = knex.avg('salary')

.from('employee')

.whereRaw('dept_no = e.dept_no')

.as('avg_sal_dept');


knex.select('e.lastname', 'e.salary', subcolumn)

.from('employee as e')

.whereRaw('dept_no = e.dept_no')

输出:

select `e`.`lastname`, `e`.`salary`, (select avg(`salary`) from `employee` where dept_no = e.dept_no) as `avg_sal_dept` from `employee` as `e` where dept_no = e.dept_no