Appearance
Prisma 高级查询
Prisma Client 提供了丰富的查询功能,支持复杂的过滤、排序、分页和聚合操作。本指南将详细介绍如何使用这些高级查询功能。
复杂过滤操作
嵌套过滤
typescript
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
// 在关系上进行过滤
async function getUsersWithPublishedPosts() {
const users = await prisma.user.findMany({
where: {
posts: {
some: { // 用户至少有一篇已发布的文章
published: true,
},
},
},
include: {
posts: {
where: {
published: true,
},
},
},
})
return users
}
// 多层嵌套过滤
async function getAuthorsWithRecentPopularPosts() {
const authors = await prisma.user.findMany({
where: {
posts: {
some: {
AND: [
{
published: true,
},
{
createdAt: {
gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // 一周内
},
},
{
likes: {
gt: 100, // 喜欢数大于100
},
},
],
},
},
},
include: {
posts: {
where: {
published: true,
createdAt: {
gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
},
},
orderBy: {
likes: 'desc',
},
},
},
})
return authors
}
高级比较操作
typescript
// 各种比较操作
async function getAdvancedComparisons() {
const posts = await prisma.post.findMany({
where: {
AND: [
{
views: {
gte: 1000, // 大于等于
lt: 10000, // 小于
},
},
{
title: {
contains: 'Prisma', // 包含
mode: 'insensitive', // 不区分大小写
},
},
{
OR: [
{
createdAt: {
gte: new Date('2023-01-01'),
lt: new Date('2023-12-31'),
},
},
{
updatedAt: {
gte: new Date(Date.now() - 24 * 60 * 60 * 1000), // 24小时内更新
},
},
],
},
],
},
orderBy: [
{ views: 'desc' }, // 首先按浏览量降序
{ createdAt: 'desc' }, // 然后按创建时间降序
],
take: 20, // 限制结果数量
})
return posts
}
聚合查询
基础聚合
typescript
// 各种聚合操作
async function getAggregations() {
// 计数聚合
const userCount = await prisma.user.count({
where: {
email: {
endsWith: 'prisma.io',
},
},
})
// 高级聚合
const postStats = await prisma.post.aggregate({
where: {
published: true,
},
_count: {
_all: true, // 计算所有记录
title: true, // 计算非空标题数量
},
_sum: {
views: true, // 求和
likes: true, // 喜欢数总和
},
_avg: {
views: true, // 平均值
likes: true,
},
_min: {
views: true, // 最小值
createdAt: true,
},
_max: {
views: true, // 最大值
createdAt: true,
},
})
return { userCount, postStats }
}
// 分组聚合
async function getGroupedAggregations() {
// 按用户分组统计文章
const userPostStats = await prisma.post.groupBy({
by: ['authorId'],
where: {
published: true,
},
_count: {
id: true, // 每个用户的发布文章数
},
_sum: {
views: true, // 每个用户的总浏览量
},
_avg: {
likes: true, // 每个用户的平均喜欢数
},
having: {
_count: {
id: {
gt: 5, // 只包括发布超过5篇文章的用户
},
},
},
})
return userPostStats
}
关联查询优化
智能关联加载
typescript
// 精确控制关联数据加载
async function getOptimizedQueries() {
const users = await prisma.user.findMany({
include: {
posts: {
select: { // 只选择需要的字段
id: true,
title: true,
published: true,
_count: {
select: { // 统计相关数据而不获取详情
comments: true,
},
},
},
where: { // 过滤关联数据
published: true,
},
orderBy: { // 排序关联数据
createdAt: 'desc',
},
take: 5, // 限制关联数据数量
},
profile: {
select: { // 只获取资料的部分字段
bio: true,
avatar: true,
},
},
},
})
return users
}
// 延迟加载模式
async function getLazyLoadingExample() {
// 首先获取用户基本信息
const users = await prisma.user.findMany({
select: {
id: true,
email: true,
name: true,
},
})
// 根据需要延迟加载关联数据
for (const user of users) {
if (user.email.endsWith('prisma.io')) {
// 只为特定用户加载额外数据
user.posts = await prisma.post.findMany({
where: {
authorId: user.id,
published: true,
},
take: 3,
})
}
}
return users
}
复杂查询模式
动态查询构建
typescript
// 动态构建查询条件
interface PostFilters {
title?: string
authorEmail?: string
minViews?: number
maxViews?: number
published?: boolean
tags?: string[]
dateFrom?: Date
dateTo?: Date
}
async function getPostsDynamic(filters: PostFilters) {
const whereClause: any = {}
if (filters.title) {
whereClause.title = {
contains: filters.title,
mode: 'insensitive',
}
}
if (filters.authorEmail) {
whereClause.author = {
email: filters.authorEmail,
}
}
if (filters.minViews !== undefined || filters.maxViews !== undefined) {
whereClause.views = {}
if (filters.minViews !== undefined) {
whereClause.views.gte = filters.minViews
}
if (filters.maxViews !== undefined) {
whereClause.views.lte = filters.maxViews
}
}
if (filters.published !== undefined) {
whereClause.published = filters.published
}
if (filters.tags && filters.tags.length > 0) {
whereClause.tags = {
some: {
name: {
in: filters.tags,
},
},
}
}
if (filters.dateFrom || filters.dateTo) {
whereClause.createdAt = {}
if (filters.dateFrom) {
whereClause.createdAt.gte = filters.dateFrom
}
if (filters.dateTo) {
whereClause.createdAt.lte = filters.dateTo
}
}
const posts = await prisma.post.findMany({
where: whereClause,
include: {
author: {
select: {
id: true,
name: true,
email: true,
},
},
},
orderBy: {
createdAt: 'desc',
},
})
return posts
}
查询缓存策略
typescript
// 简单的查询缓存实现
class QueryCache {
private cache = new Map<string, { data: any; timestamp: number; ttl: number }>()
async getCached<T>(
key: string,
queryFn: () => Promise<T>,
ttl: number = 5 * 60 * 1000 // 5分钟默认TTL
): Promise<T> {
const cached = this.cache.get(key)
if (cached && Date.now() - cached.timestamp < cached.ttl) {
console.log(`Cache hit for key: ${key}`)
return cached.data
}
console.log(`Cache miss for key: ${key}`)
const data = await queryFn()
this.cache.set(key, {
data,
timestamp: Date.now(),
ttl,
})
return data
}
invalidate(key: string) {
this.cache.delete(key)
}
}
const queryCache = new QueryCache()
// 使用缓存的热门文章查询
async function getPopularPostsCached() {
return await queryCache.getCached(
'popular-posts',
async () => {
return await prisma.post.findMany({
where: {
published: true,
views: {
gte: 1000,
},
},
include: {
author: true,
},
orderBy: [
{ views: 'desc' },
{ createdAt: 'desc' },
],
take: 10,
})
},
10 * 60 * 1000 // 10分钟TTL
)
}
性能优化查询
索引友好的查询
typescript
// 使用复合索引的查询
async function getUsersWithIndexFriendlyQuery() {
// 假设在 schema 中定义了复合索引
// @@index([email, createdAt])
const users = await prisma.user.findMany({
where: {
email: 'specific@example.com', // 使用索引的第一个字段
createdAt: {
gte: new Date('2023-01-01'),
}, // 使用索引的第二个字段
},
take: 100,
})
return users
}
// 分页查询优化
async function getPaginatedResults(page: number, pageSize: number = 10) {
const skip = (page - 1) * pageSize
// 使用 cursor-based pagination (推荐)
const posts = await prisma.post.findMany({
where: {
published: true,
},
orderBy: {
id: 'asc', // 使用稳定的排序字段
},
skip,
take: pageSize,
})
// 获取总数(用于分页信息)
const total = await prisma.post.count({
where: {
published: true,
},
})
return {
data: posts,
meta: {
page,
pageSize,
total,
totalPages: Math.ceil(total / pageSize),
},
}
}
// Cursor-based 分页
async function getCursorBasedPagination(afterCursor?: number, first: number = 10) {
const where = {
published: true,
}
const posts = await prisma.post.findMany({
where,
orderBy: {
id: 'asc',
},
cursor: afterCursor ? { id: afterCursor } : undefined,
skip: afterCursor ? 1 : 0, // 跳过游标指向的记录
take: first,
})
return posts
}
原生查询与 Prisma 查询结合
复杂统计查询
typescript
// 复杂的统计查询使用原生SQL
async function getComplexAnalytics() {
// 使用原生查询进行复杂统计
const monthlyStats = await prisma.$queryRaw<Array<{
month: string
postCount: bigint
avgViews: number
totalViews: bigint
}>>`
SELECT
DATE_TRUNC('month', "createdAt") as "month",
COUNT(*) as "postCount",
AVG("views") as "avgViews",
SUM("views") as "totalViews"
FROM "Post"
WHERE "published" = true
GROUP BY DATE_TRUNC('month', "createdAt")
ORDER BY "month" DESC
LIMIT 12
`
return monthlyStats
}
// 使用 Prisma 查询和原生查询结合
async function getCombinedQueryResults() {
// 首先用 Prisma 获取基础数据
const topAuthors = await prisma.user.findMany({
include: {
_count: {
select: {
posts: {
where: {
published: true,
},
},
},
},
},
orderBy: {
posts: {
_count: 'desc',
},
},
take: 10,
})
// 然后用原生查询获取更详细的统计
const authorStats = await prisma.$queryRaw<Array<{
userId: number
totalViews: bigint
avgLikes: number
engagementRate: number
}>>`
SELECT
"authorId" as "userId",
SUM("views") as "totalViews",
AVG("likes") as "avgLikes",
(AVG("likes") * 100.0 / AVG(CASE WHEN "views" > 0 THEN "views" ELSE 1 END)) as "engagementRate"
FROM "Post"
WHERE "authorId" IN (${Prisma.join(topAuthors.map(u => u.id))})
AND "published" = true
GROUP BY "authorId"
`
// 结合两种结果
return topAuthors.map(author => {
const stats = authorStats.find(s => s.userId === author.id)
return {
...author,
stats: stats || null,
}
})
}
查询性能监控
查询分析和优化
typescript
// 查询性能监控
async function monitoredQuery<T>(queryName: string, queryFn: () => Promise<T>) {
const startTime = Date.now()
try {
const result = await queryFn()
const duration = Date.now() - startTime
console.log(`Query "${queryName}" executed in ${duration}ms`)
// 如果查询时间过长,记录警告
if (duration > 1000) { // 超过1秒
console.warn(`Slow query detected: "${queryName}", duration: ${duration}ms`)
}
return result
} catch (error) {
const duration = Date.now() - startTime
console.error(`Query "${queryName}" failed after ${duration}ms:`, error)
throw error
}
}
// 使用监控的查询
async function getMonitoredUserPosts(userId: number) {
return await monitoredQuery(
`getUserPosts-${userId}`,
async () => {
return await prisma.post.findMany({
where: {
authorId: userId,
published: true,
},
include: {
tags: true,
},
orderBy: {
createdAt: 'desc',
},
take: 20,
})
}
)
}
通过掌握这些高级查询技巧,您可以构建高效、灵活的数据库查询,满足复杂的应用需求。