È uscito il Corso SQL Completo

Aggregazioni

Aggregazioni con Prisma

Prisma supporta operazioni di aggregazione direttamente nel client, senza dover scrivere SQL raw.

aggregate()

Il metodo aggregate() permette di calcolare valori aggregati su un modello.

_count

// Contare tutti gli utenti
const totalUsers = await prisma.user.count()

// Contare con filtri
const activeUsers = await prisma.user.count({
  where: { isActive: true },
})

// Contare campi non-null specifici
const result = await prisma.user.aggregate({
  _count: {
    _all: true,    // Totale record
    email: true,   // Record con email non-null
    bio: true,     // Record con bio non-null
  },
})
// result._count._all => 100
// result._count.bio => 45

_sum, _avg, _min, _max

const orderStats = await prisma.order.aggregate({
  _sum: {
    total: true,
    quantity: true,
  },
  _avg: {
    total: true,
  },
  _min: {
    total: true,
    createdAt: true,
  },
  _max: {
    total: true,
    createdAt: true,
  },
})

console.log('Totale vendite:', orderStats._sum.total)
console.log('Media ordine:', orderStats._avg.total)
console.log('Ordine minimo:', orderStats._min.total)
console.log('Ordine massimo:', orderStats._max.total)
console.log('Primo ordine:', orderStats._min.createdAt)

Aggregazioni con Filtri

// Statistiche solo per ordini completati dell'ultimo mese
const monthlyStats = await prisma.order.aggregate({
  where: {
    status: 'DELIVERED',
    createdAt: {
      gte: new Date('2026-01-01'),
    },
  },
  _sum: { total: true },
  _avg: { total: true },
  _count: { _all: true },
})

groupBy()

Raggruppa i risultati per uno o piu’ campi e applica aggregazioni a ciascun gruppo.

Raggruppamento Base

// Contare utenti per ruolo
const usersByRole = await prisma.user.groupBy({
  by: ['role'],
  _count: {
    _all: true,
  },
})
// [
//   { role: 'USER', _count: { _all: 80 } },
//   { role: 'ADMIN', _count: { _all: 5 } },
//   { role: 'MODERATOR', _count: { _all: 15 } },
// ]

Raggruppamento con Aggregazioni

// Vendite per categoria
const salesByCategory = await prisma.order.groupBy({
  by: ['category'],
  _sum: {
    total: true,
  },
  _avg: {
    total: true,
  },
  _count: {
    _all: true,
  },
  orderBy: {
    _sum: {
      total: 'desc',
    },
  },
})

Raggruppamento Multiplo

// Raggruppare per citta' e ruolo
const groupedUsers = await prisma.user.groupBy({
  by: ['city', 'role'],
  _count: {
    _all: true,
  },
  orderBy: {
    _count: {
      _all: 'desc',
    },
  },
})

having

Filtra i gruppi dopo l’aggregazione (equivalente SQL di HAVING):

// Citta' con piu' di 10 utenti
const popularCities = await prisma.user.groupBy({
  by: ['city'],
  _count: {
    _all: true,
  },
  having: {
    _all: {
      _count: {
        gt: 10,
      },
    },
  },
})

// Categorie con vendite totali superiori a 1000
const topCategories = await prisma.order.groupBy({
  by: ['category'],
  _sum: {
    total: true,
  },
  having: {
    total: {
      _sum: {
        gt: 1000,
      },
    },
  },
})

Filtri where e having Insieme

// where filtra PRIMA del raggruppamento
// having filtra DOPO il raggruppamento
const result = await prisma.order.groupBy({
  by: ['category'],
  where: {
    status: 'DELIVERED',           // Solo ordini consegnati
    createdAt: { gte: new Date('2026-01-01') },
  },
  _sum: { total: true },
  _count: { _all: true },
  having: {
    total: {
      _sum: { gte: 500 },         // Solo categorie con totale >= 500
    },
  },
  orderBy: {
    _sum: { total: 'desc' },
  },
})

Conteggio nelle Relazioni

Puoi includere il conteggio delle relazioni direttamente nelle query:

const usersWithCounts = await prisma.user.findMany({
  include: {
    _count: {
      select: {
        posts: true,
        comments: true,
        followers: true,
      },
    },
  },
})

// Accedere ai conteggi
usersWithCounts.forEach((user) => {
  console.log(`${user.name}: ${user._count.posts} post, ${user._count.comments} commenti`)
})

// Filtrare per conteggio di relazione
const prolificAuthors = await prisma.user.findMany({
  where: {
    posts: {
      some: {},
    },
  },
  include: {
    _count: { select: { posts: true } },
  },
  orderBy: {
    posts: { _count: 'desc' },
  },
  take: 10,
})