Escribir Queries con Select y Include Optimizados

Lectura
20 min~5 min lectura

Concepto clave

En Prisma ORM, select y include son dos métodos fundamentales para controlar qué datos se recuperan de la base de datos. Select te permite especificar exactamente qué campos de un modelo quieres obtener, mientras que include se usa para cargar relaciones entre modelos. La optimización de estas queries es crucial en producción porque afecta directamente el rendimiento de tu aplicación.

Imagina que estás en un restaurante: select es como pedir solo el plato principal sin guarniciones extras, mientras que include es pedir el menú completo con entrada, plato principal y postre. En producción, queremos ser como clientes conscientes: pedir solo lo necesario para evitar desperdiciar recursos del servidor y tiempo de respuesta.

La diferencia clave es que select trabaja a nivel de campos individuales dentro de un modelo, mientras que include maneja relaciones entre modelos. Una query optimizada combina ambos métodos estratégicamente para minimizar la transferencia de datos y maximizar la eficiencia.

Cómo funciona en la práctica

Veamos un ejemplo práctico con un esquema común: tenemos modelos User, Post y Comment. Un usuario puede tener muchos posts, y cada post puede tener muchos comentarios. En una aplicación real, rara vez necesitamos todos los campos de todos los modelos.

Paso 1: Identifica qué datos necesitas realmente. Por ejemplo, para mostrar una lista de posts en el dashboard, quizás solo necesites: título del post, fecha de creación, nombre del autor y cantidad de comentarios.

Paso 2: Usa select para limitar los campos. En lugar de traer todos los campos del usuario (email, password hash, etc.), selecciona solo el nombre.

Paso 3: Usa include solo cuando necesites datos de relaciones. Si solo necesitas contar comentarios, no incluyas todos los comentarios con sus contenidos.

Paso 4: Considera usar select dentro de include para refinar aún más qué campos traer de las relaciones.

Código en acción

Primero, veamos una query NO optimizada (el "antes"):

// Query NO optimizada - Trae demasiados datos innecesarios
const posts = await prisma.post.findMany({
  include: {
    author: true,  // Trae TODOS los campos del usuario
    comments: true // Trae TODOS los comentarios con todos sus campos
  }
});

Ahora, la versión optimizada (el "después"):

// Query OPTIMIZADA - Trae solo lo necesario
const optimizedPosts = await prisma.post.findMany({
  select: {
    id: true,
    title: true,
    createdAt: true,
    author: {
      select: {
        name: true  // Solo el nombre, no email, password, etc.
      }
    },
    _count: {
      select: {
        comments: true  // Solo la cantidad, no los comentarios completos
      }
    }
  },
  where: {
    published: true
  },
  orderBy: {
    createdAt: 'desc'
  },
  take: 20  // Limita resultados para paginación
});

Errores comunes

  • Incluir relaciones innecesarias: Usar include: true para todas las relaciones sin evaluar si realmente se necesitan. Solución: Pregúntate "¿este dato se mostrará al usuario o se usará en lógica de negocio?"
  • Traer todos los campos de modelos relacionados: Incluir un modelo completo cuando solo necesitas uno o dos campos. Solución: Usa select dentro de include para especificar campos exactos.
  • Olvidar la paginación: Hacer queries sin take o skip que pueden devolver miles de registros. Solución: Siempre implementa paginación en endpoints que listan datos.
  • No usar agregaciones cuando son suficientes: Traer arrays completos de datos para solo contar o promediar. Solución: Usa _count, _avg, _sum cuando solo necesites estadísticas.
  • Ignorar índices de base de datos: Optimizar solo a nivel Prisma sin considerar índices en campos frecuentemente filtrados. Solución: Asegúrate de tener índices en campos usados en where, orderBy y relaciones.

Checklist de dominio

  1. ¿He identificado exactamente qué campos necesito de cada modelo?
  2. ¿Estoy usando select en lugar de include cuando no necesito relaciones completas?
  3. ¿He limitado el número de resultados con take para paginación?
  4. ¿Estoy usando funciones de agregación (_count, _sum) en lugar de traer arrays completos?
  5. ¿He revisado que mis campos de filtro y ordenación tengan índices en la base de datos?
  6. ¿Estoy evitando el problema N+1 usando include estratégicamente?
  7. ¿He probado mis queries con volúmenes de datos similares a producción?

Optimiza una query real para un sistema de blog

Vas a optimizar una query existente en un sistema de blog. Sigue estos pasos:

  1. Analiza el siguiente esquema Prisma:
    model User {
      id        Int      @id @default(autoincrement())
      email     String   @unique
      name      String?
      password  String
      posts     Post[]
      createdAt DateTime @default(now())
    }
    
    model Post {
      id        Int       @id @default(autoincrement())
      title     String
      content   String
      published Boolean   @default(false)
      author    User      @relation(fields: [authorId], references: [id])
      authorId  Int
      comments  Comment[]
      tags      String[]
      createdAt DateTime  @default(now())
      updatedAt DateTime  @updatedAt
    }
    
    model Comment {
      id        Int      @id @default(autoincrement())
      content   String
      post      Post     @relation(fields: [postId], references: [id])
      postId    Int
      author    String
      createdAt DateTime @default(now())
    }
  2. La query actual para la página principal del blog es:
    const posts = await prisma.post.findMany({
      include: {
        author: true,
        comments: true
      },
      where: { published: true },
      orderBy: { createdAt: 'desc' }
    });
  3. Requisitos de la página principal:
    • Mostrar: título del post, fecha, nombre del autor, cantidad de comentarios
    • No mostrar: contenido completo, email del autor, comentarios detallados
    • Límite: 10 posts por página
    • Orden: más recientes primero
  4. Refactoriza la query usando select y include optimizados
  5. Escribe la nueva query en un archivo JavaScript/TypeScript
  6. Verifica que solo trae los datos necesarios
Pistas
  • Recuerda que puedes usar select dentro de include para relaciones
  • Considera usar _count para obtener la cantidad de comentarios sin traerlos todos
  • No olvides agregar take para limitar los resultados

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.