Knex.js 查询构建器:灵活构建 SQL 语句

FreeGuideOnline 最新 2026-06-16

Knex.js 查询构建器:优雅地构建 SQL 语句

Knex.js 是一个功能强大的 Node.js SQL 查询构建器,它为 PostgreSQL、MySQL、SQLite3 等多种数据库提供了统一的接口。通过链式调用,你可以用 JavaScript 代码灵活地构建出安全、可读的 SQL 语句,而无需手动拼接字符串。本教程将带你从零开始,掌握 Knex 查询构建器的核心用法。

初始化和数据库连接

在开始构建查询之前,需要先安装 Knex 和对应的数据库驱动,并完成配置。

npm install knex pg  # 以 PostgreSQL 为例

创建 knexfile.js 或直接通过配置对象初始化 Knex 实例:

const knex = require('knex')({
  client: 'pg',
  connection: {
    host: '127.0.0.1',
    port: 5432,
    user: 'your_user',
    password: 'your_password',
    database: 'your_database'
  }
});

连接对象也可以是一个指向环境变量的字符串,便于管理敏感信息。

基础查询语句

Knex 的查询通常以 knex('table_name') 开始,返回一个查询构建器对象,然后串联各种方法。

SELECT 查询

// 获取所有用户
knex.select('*').from('users')
// 或使用更简洁的写法
knex('users');

指定列、添加别名:

knex.select('id', 'name', 'email as user_email').from('users');

使用 where 条件筛选:

knex('users')
  .where('age', '>', 18)
  .andWhere('status', 'active');

复杂条件可以用回调函数分组:

knex('users').where(function() {
  this.where('credits', '>', 100).orWhere('vip', true);
});

INSERT 语句

knex('users').insert({
  name: 'Alice',
  email: 'alice@example.com',
  age: 28
});

批量插入传入数组:

knex('users').insert([
  { name: 'Bob', email: 'bob@example.com' },
  { name: 'Charlie', email: 'charlie@example.com' }
]);

某些数据库支持 .returning() 方法返回插入后的字段(如 PostgreSQL):

knex('users').insert({ name: 'Alice' }).returning('id');

UPDATE 语句

更新操作链式调用 .update().where()

knex('users')
  .where('id', 1)
  .update({ email: 'newalice@example.com' });

更新多列时,直接传入一个包含新值的对象。也可以使用 .increment().decrement() 方便地修改数值字段:

knex('users').where('id', 1).increment('login_count', 1);
knex('accounts').where('id', 10).decrement('balance', 50);

DELETE 语句

knex('users')
  .where('id', 5)
  .del()
  .then(affectedRows => {
    console.log(`删除了 ${affectedRows} 行`);
  });

永远不要忘记 where 条件,否则会清空整张表。为了防止误操作,可以配置 Knex 不允许不带条件的删除(部分数据库驱动支持)。

高级查询技巧

JOIN 查询

Knex 提供了直观的 join 方法:

knex('users')
  .join('posts', 'users.id', '=', 'posts.user_id')
  .select('users.name', 'posts.title');

左连接:

knex('users')
  .leftJoin('posts', 'users.id', 'posts.user_id')
  .select('users.name', 'posts.title');

多表连接和复杂的 on 条件:

knex('users')
  .join('orders', function() {
    this.on('users.id', '=', 'orders.user_id')
        .andOn('orders.status', '=', 'completed');
  });

子查询和派生表

在 where 或 from 中使用子查询:

knex('users').whereIn('id', function() {
  this.select('user_id').from('orders').where('amount', '>', 100);
});

将子查询作为派生表:

knex('users').join(
  knex('orders').select('user_id').sum('amount as total').groupBy('user_id').as('order_summary'),
  'users.id',
  'order_summary.user_id'
);

聚合和分组

knex('orders')
  .groupBy('status')
  .select('status', knex.raw('count(*) as count'));

常用聚合函数如 .sum(), .avg(), .min(), .max() 可以直接使用:

knex('products').max('price as max_price').where('category', 'electronics');

排序、分页与限制

knex('posts')
  .orderBy('created_at', 'desc')
  .limit(10)
  .offset(20); // 跳过前20条,取10条

.paginate() 方法可简化分页计算(需要额外安装或自行封装,常见用法是配合 limitoffset)。

原生表达式与事务

knex.raw 使用

当构建器无法满足需求时,可使用 knex.raw 编写安全原生 SQL:

knex('users')
  .select(knex.raw('CURRENT_DATE AS today'))
  .whereRaw('age > ? AND status = ?', [18, 'active']);

参数绑定使用 ? 占位符或 :name 命名参数(取决于数据库),防止 SQL 注入。

事务处理

通过 knex.transaction() 创建事务,自动提交或回滚:

knex.transaction(function(trx) {
  return trx('users').insert({ name: 'Eve' }).returning('id')
    .then(function([id]) {
      return trx('profiles').insert({ user_id: id, bio: 'hello' });
    });
})
.then(() => console.log('事务成功'))
.catch(err => console.log('事务回滚', err));

事务对象 trx 替代 knex 进行所有查询,确保原子性。

调试与日志

查看生成的 SQL 语句,可在查询链末尾添加 .toSQL().toString()

const query = knex('users').where('id', 1).toString();
console.log(query); // select * from "users" where "id" = 1

开发时启用 Knex 的调试模式打印所有查询:

const knex = require('knex')({
  client: 'pg',
  connection: {...},
  debug: true // 输出每条SQL到控制台
});

或者监听 query 事件:

knex.on('query', queryData => {
  console.log(queryData.sql, queryData.bindings);
});

实战示例:构建一个用户搜索 API

结合所学,完成一个支持分页和过滤的查询:

async function searchUsers({ keyword, status, page = 1, pageSize = 10 }) {
  const offset = (page - 1) * pageSize;
  const query = knex('users')
    .modify(function(qb) {
      if (keyword) {
        qb.where('name', 'ilike', `%${keyword}%`);
      }
      if (status) {
        qb.andWhere('status', status);
      }
    });

  const [totalResult, users] = await Promise.all([
    query.clone().count('id as total').first(),
    query.clone().select('*').limit(pageSize).offset(offset)
  ]);

  return {
    data: users,
    total: totalResult.total,
    page,
    pageSize
  };
}

使用 .modify() 可以保持链式调用的同时复用条件构建逻辑,.clone() 则避免查询器被提前执行影响后续操作。

总结

Knex.js 查询构建器让 JavaScript 操作数据库变得既直观又安全。无论是简单的 CRUD 还是复杂的多表关联、事务处理,它都能提供清晰的链式语法。配合连接池和迁移工具,Knex 可以胜任从小型项目到企业级应用的数据库交互需求。开始尝试在你的项目中用 Knex 替换硬编码 SQL,体验无字符串拼接的开发乐趣。