Sequelize 基础查询操作 - findAll、findOne、count等方法详解
发布时间:2024-02-12
作者:一介布衣
标签:Sequelize, 查询操作, findAll, 数据查询
前言
前面两篇文章我们学习了模型定义和实例操作,今天咱们来学习 Sequelize 的查询功能。说实话,查询是我们日常开发中用得最多的功能,掌握好了查询,基本上就掌握了 Sequelize 的核心。
我记得刚开始用 Sequelize 的时候,总是搞不清楚什么时候用 findAll
,什么时候用 findOne
,还有各种查询条件的写法,经常要翻文档。后来慢慢总结,发现其实是有规律的。
今天我就把这些查询方法和技巧整理出来,让大家能够快速上手,写出高效的查询代码。
基础查询方法
1. findAll() - 查询多条记录
这是最常用的查询方法:
javascript
// 查询所有用户
const users = await User.findAll();
// 带条件查询
const activeUsers = await User.findAll({
where: {
status: 'active'
}
});
// 查询指定字段
const users = await User.findAll({
attributes: ['id', 'name', 'email']
});
// 排除某些字段
const users = await User.findAll({
attributes: { exclude: ['password', 'deletedAt'] }
});
实际应用场景:
javascript
// 用户列表页面
async function getUserList(page = 1, limit = 10) {
const offset = (page - 1) * limit;
return await User.findAll({
attributes: ['id', 'name', 'email', 'createdAt'],
where: { status: 'active' },
order: [['createdAt', 'DESC']],
limit,
offset
});
}
2. findOne() - 查询单条记录
查询第一条符合条件的记录:
javascript
// 根据条件查询第一个用户
const user = await User.findOne({
where: { email: 'test@example.com' }
});
if (user) {
console.log('找到用户:', user.name);
} else {
console.log('用户不存在');
}
// 查询最新的用户
const latestUser = await User.findOne({
order: [['createdAt', 'DESC']]
});
3. findByPk() - 根据主键查询
这是查询单条记录最高效的方法:
javascript
// 根据主键查询
const user = await User.findByPk(1);
// 指定查询字段
const user = await User.findByPk(1, {
attributes: ['id', 'name', 'email']
});
// 包含关联数据
const user = await User.findByPk(1, {
include: ['posts', 'profile']
});
4. findAndCountAll() - 查询并统计
分页查询的最佳选择:
javascript
const result = await User.findAndCountAll({
where: { status: 'active' },
limit: 10,
offset: 0,
order: [['createdAt', 'DESC']]
});
console.log('总数:', result.count);
console.log('当前页数据:', result.rows);
// 计算分页信息
const totalPages = Math.ceil(result.count / 10);
实际分页实现:
javascript
async function getPaginatedUsers(page = 1, pageSize = 10, search = '') {
const offset = (page - 1) * pageSize;
const whereClause = {};
if (search) {
whereClause[Op.or] = [
{ name: { [Op.like]: `%\${search}%` } },
{ email: { [Op.like]: `%\${search}%` } }
];
}
const result = await User.findAndCountAll({
where: whereClause,
limit: pageSize,
offset,
order: [['createdAt', 'DESC']],
attributes: { exclude: ['password'] }
});
return {
data: result.rows,
pagination: {
total: result.count,
page,
pageSize,
totalPages: Math.ceil(result.count / pageSize)
}
};
}
5. count() - 统计记录数
javascript
// 统计所有用户
const totalUsers = await User.count();
// 带条件统计
const activeUsers = await User.count({
where: { status: 'active' }
});
// 按字段分组统计
const statusCount = await User.count({
group: 'status'
});
// 结果: [{ status: 'active', count: 10 }, { status: 'inactive', count: 5 }]
6. max() / min() / sum() - 聚合函数
javascript
// 最大值
const maxAge = await User.max('age');
// 最小值
const minAge = await User.min('age');
// 求和
const totalScore = await User.sum('score');
// 带条件的聚合
const avgAge = await User.aggregate('age', 'avg', {
where: { status: 'active' }
});
查询条件详解
1. 基本条件
javascript
// 等于
const users = await User.findAll({
where: { status: 'active' }
});
// 多个条件(AND)
const users = await User.findAll({
where: {
status: 'active',
age: 25
}
});
// 数组条件(IN)
const users = await User.findAll({
where: {
id: [1, 2, 3, 4, 5]
}
});
2. 操作符条件
javascript
const { Op } = require('sequelize');
// 比较操作符
const users = await User.findAll({
where: {
age: {
[Op.gt]: 18, // 大于
[Op.gte]: 18, // 大于等于
[Op.lt]: 65, // 小于
[Op.lte]: 65, // 小于等于
[Op.ne]: null // 不等于
}
}
});
// 范围查询
const users = await User.findAll({
where: {
age: {
[Op.between]: [18, 65] // 在范围内
},
status: {
[Op.in]: ['active', 'pending'] // 在列表中
},
email: {
[Op.notIn]: ['banned@example.com'] // 不在列表中
}
}
});
// 模糊查询
const users = await User.findAll({
where: {
name: {
[Op.like]: '%张%', // 包含
[Op.notLike]: '%admin%', // 不包含
[Op.iLike]: '%ZHANG%', // 忽略大小写包含(PostgreSQL)
[Op.startsWith]: '张', // 以...开始
[Op.endsWith]: '三', // 以...结束
[Op.substring]: '张三' // 包含子字符串
}
}
});
// 空值查询
const users = await User.findAll({
where: {
deletedAt: {
[Op.is]: null // 是 null
},
avatar: {
[Op.not]: null // 不是 null
}
}
});
3. 逻辑操作符
javascript
// OR 条件
const users = await User.findAll({
where: {
[Op.or]: [
{ status: 'active' },
{ isVip: true }
]
}
});
// 复杂逻辑组合
const users = await User.findAll({
where: {
[Op.and]: [
{
[Op.or]: [
{ status: 'active' },
{ status: 'pending' }
]
},
{
age: { [Op.gte]: 18 }
}
]
}
});
// NOT 条件
const users = await User.findAll({
where: {
[Op.not]: {
status: 'banned'
}
}
});
4. 日期查询
javascript
// 日期比较
const users = await User.findAll({
where: {
createdAt: {
[Op.gte]: new Date('2024-01-01'),
[Op.lt]: new Date('2024-12-31')
}
}
});
// 相对日期
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
const recentUsers = await User.findAll({
where: {
lastLoginAt: {
[Op.gte]: thirtyDaysAgo
}
}
});
// 日期函数(MySQL)
const todayUsers = await User.findAll({
where: sequelize.where(
sequelize.fn('DATE', sequelize.col('createdAt')),
new Date().toISOString().split('T')[0]
)
});
排序和限制
1. 排序(ORDER BY)
javascript
// 单字段排序
const users = await User.findAll({
order: [['createdAt', 'DESC']]
});
// 多字段排序
const users = await User.findAll({
order: [
['status', 'ASC'],
['createdAt', 'DESC']
]
});
// 关联表排序
const users = await User.findAll({
include: ['posts'],
order: [
['name', 'ASC'],
['posts', 'createdAt', 'DESC']
]
});
// 函数排序
const users = await User.findAll({
order: [
sequelize.fn('RAND') // 随机排序(MySQL)
]
});
2. 限制和偏移(LIMIT & OFFSET)
javascript
// 限制数量
const users = await User.findAll({
limit: 10
});
// 分页
const users = await User.findAll({
limit: 10,
offset: 20 // 跳过前20条
});
// 实用的分页函数
function getPagination(page, size) {
const limit = size ? +size : 10;
const offset = page ? page * limit : 0;
return { limit, offset };
}
const { limit, offset } = getPagination(2, 10);
const users = await User.findAll({ limit, offset });
字段选择和别名
1. 选择特定字段
javascript
// 只查询指定字段
const users = await User.findAll({
attributes: ['id', 'name', 'email']
});
// 排除某些字段
const users = await User.findAll({
attributes: { exclude: ['password', 'deletedAt'] }
});
// 字段别名
const users = await User.findAll({
attributes: [
'id',
['name', 'userName'], // name 字段别名为 userName
['email', 'userEmail']
]
});
2. 计算字段
javascript
// 聚合计算
const users = await User.findAll({
attributes: [
'id',
'name',
[sequelize.fn('COUNT', sequelize.col('posts.id')), 'postCount']
],
include: ['posts'],
group: ['User.id']
});
// 字符串拼接
const users = await User.findAll({
attributes: [
'id',
[sequelize.fn('CONCAT', sequelize.col('firstName'), ' ', sequelize.col('lastName')), 'fullName']
]
});
// 条件字段
const users = await User.findAll({
attributes: [
'id',
'name',
[
sequelize.literal(`CASE WHEN age >= 18 THEN 'adult' ELSE 'minor' END`),
'ageGroup'
]
]
});
分组查询
1. 基本分组
javascript
// 按状态分组统计
const statusStats = await User.findAll({
attributes: [
'status',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['status']
});
// 按日期分组
const dailyStats = await User.findAll({
attributes: [
[sequelize.fn('DATE', sequelize.col('createdAt')), 'date'],
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: [sequelize.fn('DATE', sequelize.col('createdAt'))]
});
2. HAVING 条件
javascript
// 查询文章数量大于5的用户
const activeAuthors = await User.findAll({
attributes: [
'id',
'name',
[sequelize.fn('COUNT', sequelize.col('posts.id')), 'postCount']
],
include: ['posts'],
group: ['User.id'],
having: sequelize.where(
sequelize.fn('COUNT', sequelize.col('posts.id')),
Op.gt,
5
)
});
原始查询
1. 使用 raw 选项
javascript
// 返回原始数据,不创建模型实例
const users = await User.findAll({
raw: true,
attributes: ['id', 'name', 'email']
});
// 性能对比
console.time('with instances');
const usersWithInstances = await User.findAll();
console.timeEnd('with instances');
console.time('raw data');
const rawUsers = await User.findAll({ raw: true });
console.timeEnd('raw data');
2. 直接 SQL 查询
javascript
// 简单查询
const users = await sequelize.query(
'SELECT id, name, email FROM users WHERE status = ?',
{
replacements: ['active'],
type: QueryTypes.SELECT
}
);
// 复杂统计查询
const stats = await sequelize.query(`
SELECT
DATE(created_at) as date,
COUNT(*) as user_count,
COUNT(CASE WHEN status = 'active' THEN 1 END) as active_count
FROM users
WHERE created_at >= :startDate
GROUP BY DATE(created_at)
ORDER BY date DESC
`, {
replacements: { startDate: '2024-01-01' },
type: QueryTypes.SELECT
});
性能优化技巧
1. 索引优化
javascript
// 确保查询字段有索引
const users = await User.findAll({
where: {
email: 'test@example.com', // email 字段应该有索引
status: 'active' // status 字段应该有索引
}
});
// 复合索引查询
const users = await User.findAll({
where: {
status: 'active',
createdAt: {
[Op.gte]: new Date('2024-01-01')
}
}
// 应该在 (status, created_at) 上建立复合索引
});
2. 查询优化
javascript
// 避免查询不必要的字段
const users = await User.findAll({
attributes: ['id', 'name'], // 只查询需要的字段
where: { status: 'active' }
});
// 使用 EXISTS 代替 IN(大数据量时)
const usersWithPosts = await User.findAll({
where: {
id: {
[Op.in]: sequelize.literal(`
(SELECT DISTINCT user_id FROM posts WHERE status = 'published')
`)
}
}
});
3. 分页优化
javascript
// 大偏移量优化
async function getOptimizedPagination(lastId = 0, limit = 10) {
return await User.findAll({
where: {
id: { [Op.gt]: lastId } // 使用游标分页
},
order: [['id', 'ASC']],
limit
});
}
实战案例
用户搜索功能
javascript
async function searchUsers(params) {
const { keyword, status, ageRange, page = 1, limit = 10 } = params;
const whereClause = {};
// 关键词搜索
if (keyword) {
whereClause[Op.or] = [
{ name: { [Op.like]: `%\${keyword}%` } },
{ email: { [Op.like]: `%\${keyword}%` } }
];
}
// 状态筛选
if (status) {
whereClause.status = status;
}
// 年龄范围
if (ageRange) {
whereClause.age = {
[Op.between]: ageRange
};
}
const { count, rows } = await User.findAndCountAll({
where: whereClause,
attributes: { exclude: ['password'] },
order: [['createdAt', 'DESC']],
limit,
offset: (page - 1) * limit
});
return {
users: rows,
total: count,
page,
totalPages: Math.ceil(count / limit)
};
}
数据统计报表
javascript
async function getUserStatistics(startDate, endDate) {
// 用户注册趋势
const registrationTrend = await User.findAll({
attributes: [
[sequelize.fn('DATE', sequelize.col('createdAt')), 'date'],
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
where: {
createdAt: {
[Op.between]: [startDate, endDate]
}
},
group: [sequelize.fn('DATE', sequelize.col('createdAt'))],
order: [[sequelize.fn('DATE', sequelize.col('createdAt')), 'ASC']],
raw: true
});
// 用户状态分布
const statusDistribution = await User.findAll({
attributes: [
'status',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['status'],
raw: true
});
// 活跃用户统计
const activeUsers = await User.count({
where: {
lastLoginAt: {
[Op.gte]: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
}
}
});
return {
registrationTrend,
statusDistribution,
activeUsers
};
}
总结
今天我们深入学习了 Sequelize 的基础查询操作:
- ✅ 各种查询方法的使用场景和技巧
- ✅ 丰富的查询条件和操作符
- ✅ 排序、分页、分组等高级功能
- ✅ 性能优化的最佳实践
- ✅ 实际项目中的应用案例
掌握了这些知识,你就能够:
- 编写高效的数据查询代码
- 处理复杂的业务查询需求
- 优化查询性能
- 构建完整的搜索和统计功能
下一篇文章,我们将学习更高级的查询方法,包括子查询、联合查询等复杂操作。
相关文章推荐:
有问题欢迎留言讨论,我会及时回复大家!