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