6.4 KiB
6.4 KiB
Supabase 查询数据数量的方法
概述
在 Supabase 中,有多种方法可以查询符合条件的数据数量。本文档将介绍各种方法的用法和适用场景。
方法一:使用 count + head 选项(推荐)
这是 Supabase 官方推荐的方法,使用 HEAD 请求只获取数量信息,不返回实际数据。
JavaScript SDK 用法
const { count, error } = await supabase
.from('table')
.select('*', { count: 'exact', head: true })
.eq('status', 'active')
我们的 UTS 实现
const result = await supa
.from('ak_assignments')
.select('*', { count: 'exact', head: true })
.eq('teacher_id', userId)
.execute()
const count = result.count // 从 count 属性获取数量
特点
- ✅ 性能最佳:使用 HEAD 请求,不传输数据
- ✅ 网络开销小:只返回 count,不返回行数据
- ✅ 官方推荐:符合 Supabase 最佳实践
- ✅ 类型安全:返回
{ count: number }
方法二:使用 count 选项(获取数据+数量)
当你既需要数据又需要数量时使用此方法。
const result = await supa
.from('ak_assignments')
.select('*', { count: 'exact' })
.eq('teacher_id', userId)
.limit(10)
.execute()
const data = result.data // 实际数据
const total = result.total // 总数量
特点
- ✅ 一次请求:同时获取数据和总数
- ✅ 适合分页:支持 limit/offset 分页
- ❌ 网络开销大:返回完整数据
方法三:聚合函数(PostgREST)
使用 PostgreSQL 聚合函数直接在数据库层面计算。
// 注意:这个语法在当前版本可能不完全支持
const result = await supa
.from('ak_assignments')
.select('id.count()')
.eq('teacher_id', userId)
.execute()
const count = result.data[0]?.count
特点
- ✅ 灵活性高:支持复杂聚合
- ❌ 语法复杂:需要了解 PostgREST 语法
- ❌ 兼容性:部分客户端不完全支持
Count 选项说明
exact(精确计数)
{ count: 'exact' }
- 执行
COUNT(*)查询 - 结果完全准确
- 性能开销较大(大表)
estimated(估算计数)
{ count: 'estimated' }
- 使用 PostgreSQL 统计信息估算
- 结果是近似值
- 性能开销很小
planned(预估计数)
{ count: 'planned' }
- 使用查询计划器估算
- 结果是预估值
- 性能开销最小
Head 模式详解
HTTP 方法差异
普通请求(GET):
GET /rest/v1/table?select=*&count=exact
- 返回数据 + count
- 传输完整行数据
Head 请求(HEAD):
HEAD /rest/v1/table?select=*&count=exact
- 只返回 headers(包含 count)
- 不传输行数据
响应格式对比
普通模式:
{
data: [...], // 实际数据
count: 42, // 总数
total: 42,
status: 200
}
Head 模式:
{
data: null, // head 模式不返回数据
count: 42, // 从 headers 解析的总数
total: 42,
status: 200
}
实际应用示例
教师仪表板统计
const loadTeacherStats = async (teacherId: string) => {
try {
// 并行执行多个 count 查询
const [totalAssignments, completedAssignments, pendingReview] = await Promise.all([
// 总作业数
supa.from('ak_assignments')
.select('*', { count: 'exact', head: true })
.eq('teacher_id', teacherId)
.execute(),
// 已完成作业数
supa.from('ak_assignments')
.select('*', { count: 'exact', head: true })
.eq('teacher_id', teacherId)
.eq('status', 'completed')
.execute(),
// 待批改作业数
supa.from('ak_assignments')
.select('*', { count: 'exact', head: true })
.eq('teacher_id', teacherId)
.eq('status', 'submitted')
.execute()
])
return {
total: totalAssignments.count ?? 0,
completed: completedAssignments.count ?? 0,
pending: pendingReview.count ?? 0
}
} catch (error) {
console.error('加载统计失败:', error)
return { total: 0, completed: 0, pending: 0 }
}
}
分页列表(数据+总数)
const loadAssignmentList = async (page: number, pageSize: number) => {
const result = await supa
.from('ak_assignments')
.select('*', { count: 'exact' })
.eq('teacher_id', userId)
.order('created_at', { ascending: false })
.range((page - 1) * pageSize, page * pageSize - 1)
.execute()
return {
data: result.data,
total: result.total,
hasMore: result.hasmore
}
}
性能建议
1. 大表查询使用 estimated
// 大表(100万+行)使用估算
const result = await supa
.from('large_table')
.select('*', { count: 'estimated', head: true })
.execute()
2. 频繁查询使用缓存
const countCache = new Map()
const getCachedCount = async (key: string, queryFn: () => Promise<any>) => {
if (countCache.has(key)) {
return countCache.get(key)
}
const result = await queryFn()
countCache.set(key, result.count)
// 5分钟后清除缓存
setTimeout(() => countCache.delete(key), 5 * 60 * 1000)
return result.count
}
3. 添加适当索引
-- 为经常查询的条件添加索引
CREATE INDEX idx_assignments_teacher_status ON ak_assignments(teacher_id, status);
CREATE INDEX idx_users_role ON ak_users(role);
错误处理
const safeGetCount = async (queryBuilder: any): Promise<number> => {
try {
const result = await queryBuilder.execute()
if (result.status >= 200 && result.status < 300) {
return result.count ?? 0
}
console.warn('Count 查询返回错误状态:', result.status)
return 0
} catch (error) {
console.error('Count 查询异常:', error)
return 0
}
}
// 使用示例
const totalCount = await safeGetCount(
supa.from('ak_assignments')
.select('*', { count: 'exact', head: true })
.eq('teacher_id', userId)
)
总结
对于 只需要数量 的场景:
- ✅ 首选:
select('*', { count: 'exact', head: true }) - ✅ 大表:
select('*', { count: 'estimated', head: true })
对于 需要数据+数量 的场景:
- ✅ 首选:
select('*', { count: 'exact' })
这样的实现既保证了性能,又符合 Supabase 的最佳实践。