0Pricing
SQL Interview Prep · Lección

Truncar y agrupar fechas por intervalos

Agrupar por semana, mes y trimestre con DATE_TRUNC y sus equivalentes.

Truncar y agrupar fechas por intervalos es una lección gratuita de SQL Interview Prep en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de SQL Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de SQL Interview Prep incluye 4 lecciones en total.

Por qué preguntan sobre la agrupación de fechas

«Mostrar los ingresos por semana» o «usuarios activos por mes» es lo habitual en las entrevistas para analistas. Lo que se evalúa es reducir marcas de tiempo precisas a un intervalo más amplio para que las filas se agrupen.

El error que cometen los perfiles junior es extraer solo el número del mes, lo que combina el mismo mes de distintos años. La respuesta profesional es el truncamiento: asignar cada marca de tiempo al inicio de su periodo.

  • Intervalos por semana, mes, trimestre y año
  • DATE_TRUNC y sus equivalentes según el dialecto
  • Agrupar correctamente para que los gráficos queden alineados

DATE_TRUNC: la herramienta fundamental

En PostgreSQL, DATE_TRUNC(unit, ts) pone a cero todo lo que sea más preciso que la unidad indicada. Truncar a 'month' convierte cualquier marca de tiempo de marzo en 2024-03-01 00:00:00.

El valor devuelto sigue siendo una marca de tiempo, por lo que se ordena cronológicamente y se agrupa perfectamente. Esta es la función de fecha más útil para generar informes.

SELECT DATE_TRUNC('month', TIMESTAMP '2024-03-17 14:30:00');
-- 2024-03-01 00:00:00

Agrupar los ingresos por mes

Este es el ejemplo práctico clásico. Trunque la marca de tiempo al mes y, después, agrupe y calcule la suma. Como el intervalo conserva el año, enero de 2023 y enero de 2024 permanecen separados.

Ordenar por el valor truncado produce una serie temporal clara, lista para incluirla en un gráfico.

SELECT
  DATE_TRUNC('month', order_ts) AS month,
  SUM(amount)                   AS revenue
FROM orders
GROUP BY 1
ORDER BY 1;

EXTRACT frente a DATE_TRUNC

Los entrevistadores suelen preguntar directamente por esta diferencia. Ambas funciones extraen información del periodo, pero responden a preguntas distintas.

  • EXTRACT(MONTH FROM ts) devuelve el número 3 para cualquier mes de marzo, sin importar el año, por lo que resulta útil para analizar la estacionalidad.
  • DATE_TRUNC('month', ts) devuelve el inicio del mes específico, conserva los años separados y resulta útil para las series temporales.

Si agrupa por EXTRACT(MONTH ...) para crear un gráfico de tendencia mensual, combinará silenciosamente los distintos años.

-- Seasonality: which month is busiest on average?
SELECT EXTRACT(MONTH FROM order_ts) AS month_num, COUNT(*)
FROM orders GROUP BY 1 ORDER BY 1;

-- Time series: month-by-month trend (years kept separate)
SELECT DATE_TRUNC('month', order_ts) AS month, COUNT(*)
FROM orders GROUP BY 1 ORDER BY 1;

Intervalos semanales y la cuestión del lunes

Agrupar por semanas oculta una sutileza que les gusta plantear a los entrevistadores: ¿cuándo comienza la semana? PostgreSQL hace que DATE_TRUNC('week', ts) siempre retroceda hasta el lunes (semanas ISO).

Si la empresa necesita semanas que comiencen el domingo, deberá aplicar un desplazamiento. Un método habitual consiste en retrasar la fecha un día, truncarla y después adelantarla de nuevo.

-- ISO week (Monday start)
SELECT DATE_TRUNC('week', order_ts) AS iso_week FROM orders;

-- Sunday-start week
SELECT DATE_TRUNC('week', order_ts + INTERVAL '1 day') - INTERVAL '1 day'
  AS sunday_week
FROM orders;

Intervalos trimestrales

Los informes trimestrales son habituales en puestos relacionados con las finanzas. DATE_TRUNC('quarter', ts) asigna cualquier marca de tiempo al primer día de su trimestre: 1 de enero, 1 de abril, 1 de julio o 1 de octubre.

Para etiquetar el trimestre con un número, combine EXTRACT(QUARTER ...) con el año.

SELECT
  DATE_TRUNC('quarter', order_ts)                  AS quarter_start,
  EXTRACT(YEAR FROM order_ts) || '-Q'
    || EXTRACT(QUARTER FROM order_ts)              AS quarter_label,
  SUM(amount)                                      AS revenue
FROM orders
GROUP BY 1, 2
ORDER BY 1;

MySQL no tiene DATE_TRUNC

Una pregunta habitual sobre las diferencias entre dialectos es: «MySQL no tiene DATE_TRUNC; ¿cómo agruparía por mes?». La respuesta portable consiste en dar formato a la fecha con la granularidad que necesite.

  • DATE_FORMAT(ts, '%Y-%m-01') devuelve el inicio del mes como texto o fecha.
  • DATE_FORMAT(ts, '%Y-%m') devuelve una clave de texto ordenable como 2024-03.

Para las semanas, MySQL ofrece YEARWEEK() con un argumento de modo que controla el inicio de la semana.

-- MySQL month bucket
SELECT DATE_FORMAT(order_ts, '%Y-%m-01') AS month, SUM(amount)
FROM orders
GROUP BY 1
ORDER BY 1;

Agrupación por periodos en SQL Server

Tradicionalmente, SQL Server no tenía una función de truncamiento directa, por lo que se utilizaban DATEFROMPARTS o el método idiomático de DATEADD/DATEDIFF. Las versiones modernas (2022 o posteriores) incorporan DATETRUNC.

El método idiomático clásico, «contar las unidades desde la época y después volver a sumarlas», funciona en todas las versiones y conviene conocerlo.

-- Portable SQL Server month truncation
SELECT DATEADD(month, DATEDIFF(month, 0, order_ts), 0) AS month_start
FROM orders;

-- SQL Server 2022+
SELECT DATETRUNC(month, order_ts) AS month_start FROM orders;

Completar los huecos de una serie temporal

El truncamiento por sí solo elimina los periodos que no tienen filas: un mes sin pedidos simplemente no aparecerá. Los entrevistadores evalúan si se da cuenta de ello.

La solución consiste en generar un calendario completo de periodos y hacer un LEFT JOIN de los datos sobre él. En Postgres, generate_series construye ese calendario.

SELECT
  cal.month,
  COALESCE(SUM(o.amount), 0) AS revenue
FROM generate_series(DATE '2024-01-01', DATE '2024-12-01',
                      INTERVAL '1 month') AS cal(month)
LEFT JOIN orders o
  ON DATE_TRUNC('month', o.order_ts) = cal.month
GROUP BY cal.month
ORDER BY cal.month;

Ejemplo avanzado: usuarios activos por semana

Combine la agrupación por periodos con un recuento de valores distintos. «Usuarios activos semanales» significa contar los usuarios distintos de cada intervalo semanal, una necesidad real de análisis de producto.

Trunque la marca de tiempo del evento a la semana y, después, use COUNT(DISTINCT user_id). Mencionar que uniría un calendario de semanas para mostrar las semanas sin actividad le dará puntos adicionales.

SELECT
  DATE_TRUNC('week', event_ts) AS week,
  COUNT(DISTINCT user_id)      AS wau
FROM events
GROUP BY 1
ORDER BY 1;

Agrupar sobre una columna indexada

Conviene mencionar una consideración de rendimiento: envolver la columna de fecha en DATE_TRUNC dentro de una cláusula WHERE puede impedir que el planificador utilice un índice sobre esa columna.

Esto está bien en GROUP BY, pero para filtrar, compare la columna sin transformar con límites calculados. Ya vimos este patrón de intervalos semiabiertos; también se aplica aquí.

-- Avoid in WHERE: DATE_TRUNC('month', order_ts) = '2024-03-01'
-- Prefer:
SELECT * FROM orders
WHERE order_ts >= DATE '2024-03-01'
  AND order_ts <  DATE '2024-04-01';

Comprobación rápida

Elija la herramienta adecuada para un gráfico de tendencia mensual que mantenga separados los años.

Repaso: truncar y agrupar fechas

Recuerde lo siguiente:

  • DATE_TRUNC(unit, ts) asigna las marcas de tiempo al inicio de un periodo y mantiene separados los años, por lo que es la herramienta adecuada para las series temporales.
  • EXTRACT devuelve un número sin más, útil para la estacionalidad, pero combina los distintos años.
  • En Postgres, las semanas comienzan el lunes; aplique un desplazamiento si necesita que comiencen el domingo.
  • MySQL utiliza DATE_FORMAT; las versiones antiguas de SQL Server utilizan el método idiomático DATEADD(DATEDIFF(...)); a partir de la versión 2022 existe DATETRUNC.
  • Use un calendario de fechas generado + LEFT JOIN para mostrar los periodos vacíos y mantenga DATE_TRUNC fuera de WHERE para conservar el uso de índices.

Preguntas frecuentes

¿La lección «Truncar y agrupar fechas por intervalos» es gratis?

Sí — el texto completo de «Truncar y agrupar fechas por intervalos» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de SQL Interview Prep, actualiza a CoddyKit PRO. El curso de SQL Interview Prep incluye 4 lecciones en total.

¿Qué aprenderé en «Truncar y agrupar fechas por intervalos»?

Agrupar por semana, mes y trimestre con DATE_TRUNC y sus equivalentes. Practicas SQL Interview Prep con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar SQL Interview Prep?

No se requiere experiencia previa. SQL Interview Prep en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Truncar y agrupar fechas por intervalos»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de SQL Interview Prep?

Sí. Cada lección de SQL Interview Prep incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Aritmética de fechas e intervalos
  2. Truncar y agrupar fechas por intervalos
  3. Analizar y dar formato a cadenas
  4. Zonas horarias y marcas de tiempo
← Volver a SQL Interview Prep