# Supabase 查询数据数量的方法 ## 概述 在 Supabase 中,有多种方法可以查询符合条件的数据数量。本文档将介绍各种方法的用法和适用场景。 ## 方法一:使用 count + head 选项(推荐) 这是 Supabase 官方推荐的方法,使用 HEAD 请求只获取数量信息,不返回实际数据。 ### JavaScript SDK 用法 ```javascript const { count, error } = await supabase .from('table') .select('*', { count: 'exact', head: true }) .eq('status', 'active') ``` ### 我们的 UTS 实现 ```typescript 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 选项(获取数据+数量) 当你既需要数据又需要数量时使用此方法。 ```typescript 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 聚合函数直接在数据库层面计算。 ```typescript // 注意:这个语法在当前版本可能不完全支持 const result = await supa .from('ak_assignments') .select('id.count()') .eq('teacher_id', userId) .execute() const count = result.data[0]?.count ``` ### 特点 - ✅ **灵活性高**:支持复杂聚合 - ❌ **语法复杂**:需要了解 PostgREST 语法 - ❌ **兼容性**:部分客户端不完全支持 ## Count 选项说明 ### exact(精确计数) ```typescript { count: 'exact' } ``` - 执行 `COUNT(*)` 查询 - 结果完全准确 - 性能开销较大(大表) ### estimated(估算计数) ```typescript { count: 'estimated' } ``` - 使用 PostgreSQL 统计信息估算 - 结果是近似值 - 性能开销很小 ### planned(预估计数) ```typescript { count: 'planned' } ``` - 使用查询计划器估算 - 结果是预估值 - 性能开销最小 ## Head 模式详解 ### HTTP 方法差异 **普通请求(GET):** ``` GET /rest/v1/table?select=*&count=exact ``` - 返回数据 + count - 传输完整行数据 **Head 请求(HEAD):** ``` HEAD /rest/v1/table?select=*&count=exact ``` - 只返回 headers(包含 count) - 不传输行数据 ### 响应格式对比 **普通模式:** ```typescript { data: [...], // 实际数据 count: 42, // 总数 total: 42, status: 200 } ``` **Head 模式:** ```typescript { data: null, // head 模式不返回数据 count: 42, // 从 headers 解析的总数 total: 42, status: 200 } ``` ## 实际应用示例 ### 教师仪表板统计 ```typescript 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 } } } ``` ### 分页列表(数据+总数) ```typescript 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 ```typescript // 大表(100万+行)使用估算 const result = await supa .from('large_table') .select('*', { count: 'estimated', head: true }) .execute() ``` ### 2. 频繁查询使用缓存 ```typescript const countCache = new Map() const getCachedCount = async (key: string, queryFn: () => Promise) => { 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. 添加适当索引 ```sql -- 为经常查询的条件添加索引 CREATE INDEX idx_assignments_teacher_status ON ak_assignments(teacher_id, status); CREATE INDEX idx_users_role ON ak_users(role); ``` ## 错误处理 ```typescript const safeGetCount = async (queryBuilder: any): Promise => { 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 的最佳实践。