Optimize database queries to improve application performance, reduce response times, and minimize database load through efficient query patterns, indexing, and monitoring.
- Identify and eliminate N+1 query patterns
- Implement eager loading where appropriate
- Use batch loading techniques
- Optimize data fetching strategies
- Analyze query patterns and identify slow queries
- Create appropriate database indexes
- Monitor index usage and effectiveness
- Maintain index performance over time
- Enable database slow query logging
- Configure appropriate slow query thresholds
- Implement query analysis and reporting
- Set up alerts for slow queries
- Optimize query structure and joins
- Implement query result caching where appropriate
- Use database-specific optimization features
- Regular performance audits and improvements
// Bad: N+1 queries
const users = await prisma.user.findMany();
for (const user of users) {
const properties = await prisma.property.findMany({
where: { ownerId: user.id }
});
}
// Good: Single query with include
const usersWithProperties = await prisma.user.findMany({
include: {
properties: true
}
});const users = await prisma.user.findMany({
select: {
id: true,
name: true,
properties: {
select: {
id: true,
title: true
}
}
}
});// Implement DataLoader for batch loading
export class PropertyLoader {
async loadMany(ownerIds: number[]): Promise<Property[][]> {
const properties = await prisma.property.findMany({
where: { ownerId: { in: ownerIds } }
});
return ownerIds.map(id =>
properties.filter(prop => prop.ownerId === id)
);
}
}-- Check existing indexes
SELECT * FROM pg_indexes WHERE tablename = 'user';
-- Analyze query performance
EXPLAIN ANALYZE SELECT * FROM user WHERE email = 'test@example.com';-- Index for user authentication
CREATE INDEX idx_user_email_active ON "user"(email, is_active);
-- Index for property searches
CREATE INDEX idx_property_location_price ON property(location, price DESC);
-- Index for trust score queries
CREATE INDEX idx_trust_score_user_date ON trust_score(user_id, created_at DESC);-- Reindex periodically
REINDEX INDEX idx_user_email_active;
-- Monitor index usage
SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read, idx_tup_fetch
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC;-- In postgresql.conf
log_min_duration_statement = 1000 -- Log queries taking > 1 second
log_statement = 'ddl' -- Log DDL statements
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '@Injectable()
export class QueryLoggerService {
private readonly logger = new Logger('QueryLogger');
async logSlowQuery(query: string, duration: number): Promise<void> {
this.logger.warn(`Slow query detected: ${duration}ms`, {
query: query.substring(0, 500),
duration
});
}
}
// Prisma middleware for query logging
prisma.$use(async (params, next) => {
const start = Date.now();
const result = await next(params);
const duration = Date.now() - start;
if (duration > 1000) { // Log queries > 1s
await queryLogger.logSlowQuery(params.model + '.' + params.action, duration);
}
return result;
});// Use findFirst for single records
const user = await prisma.user.findFirst({
where: { email: email }
});
// Use count for counting
const totalUsers = await prisma.user.count();
// Use aggregation for calculations
const avgPrice = await prisma.property.aggregate({
_avg: { price: true }
});@Injectable()
export class CacheService {
async getCachedQuery<T>(
key: string,
queryFn: () => Promise<T>,
ttl: number = 300
): Promise<T> {
const cached = await this.redis.get(key);
if (cached) return JSON.parse(cached);
const result = await queryFn();
await this.redis.setex(key, ttl, JSON.stringify(result));
return result;
}
}
// Usage
const properties = await this.cacheService.getCachedQuery(
`properties:${userId}`,
() => prisma.property.findMany({ where: { ownerId: userId } })
);// Use raw SQL for complex aggregations when necessary
const complexQuery = await prisma.$queryRaw`
SELECT
u.name,
COUNT(p.id) as property_count,
AVG(p.price) as avg_price
FROM "user" u
LEFT JOIN property p ON u.id = p.owner_id
GROUP BY u.id, u.name
HAVING COUNT(p.id) > 0
`;- Query execution time
- Database connection pool usage
- Cache hit/miss ratios
- Index usage statistics
- Monthly query performance review
- Index maintenance and cleanup
- Cache strategy evaluation
- Database vacuum and analyze operations
- Unit tests for query optimization logic
- Integration tests with realistic data volumes
- Performance benchmarks before/after optimization
- Load testing with concurrent users