Informe del panel - Me gusta

Esta es una versión SQL del Informe del Panel de Control de Me gusta.

Esta consulta proporciona un informe del número total de me gusta dados a todas las publicaciones en un sitio, día a día, dentro de un rango de fechas especificado.

-- [params]
-- date :start_date = 2023-12-08
-- date :end_date = 2024-01-10

WITH date_range AS (
  SELECT date_trunc('day', series) AS date
  FROM generate_series(
    :start_date::timestamp,
    :end_date::timestamp,
    '1 day'::interval
  ) series
)

SELECT
  dr.date::date,
  COALESCE(pa.likes_count, 0) AS likes_count
FROM
  date_range dr
LEFT JOIN (
  SELECT
    date_trunc('day', pa.created_at) AS action_date,
    COUNT(*) AS likes_count
  FROM post_actions pa
  WHERE pa.post_action_type_id = 2
    AND pa.created_at >= :start_date
    AND pa.created_at <= (:end_date::date + 1)
  GROUP BY action_date
) pa ON dr.date = pa.action_date
ORDER BY dr.date

Explicación de la consulta SQL

La estructura principal de la consulta se basa en una CTE (Expresión Común de Tabla) llamada date_range, que se utiliza para generar una serie de marcas de tiempo, cada una representando un día diferente dentro del período definido por el usuario.

Parámetros

La consulta acepta dos parámetros:

  • :start_date: El comienzo del período para el cual generar el informe.
  • :end_date: El final del período para el cual generar el informe.

Expresión Común de Tabla: date_range

  • generate_series es una función que crea un conjunto de marcas de tiempo desde :start_date hasta :end_date, incrementadas por un intervalo de ‘1 día’.
  • date_trunc('day', series) trunca la marca de tiempo al inicio del día, normalizando efectivamente todas las marcas de tiempo a las 00:00:00 de sus respectivos días.
  • El resultado es un conjunto de fechas, una por fila, que cubre todo el rango desde :start_date hasta :end_date.

Subconsulta: Contando Me gusta

Se utiliza una subconsulta para calcular el número de me gusta para cada día contando filas de la tabla post_actions.

  • Esta consulta filtra post_actions para entradas donde el tipo de acción significa un me gusta (donde post_action_type_id = 2 significa ‘me gusta’).
  • Filtra las acciones al rango de fechas, agregando un día a la fecha de finalización para incluir los me gusta dados en el último día.
  • Agrupa los resultados por día y cuenta los me gusta para cada día.

Consulta Principal: Combinando Resultados

La sección final de la consulta combina el conjunto de todas las fechas de la CTE date_range con el recuento de me gusta de la subconsulta.

  • Un LEFT JOIN asegura que todas las fechas de date_range se incluyan en el resultado, incluso si no hay acciones de me gusta correspondientes para una fecha determinada (no se encontraron uniones en la subconsulta).
  • COALESCE se utiliza para reemplazar los recuentos NULL (de días sin me gusta) con cero, asegurando que el informe refleje con precisión los días sin actividad de me gusta.
  • El conjunto de resultados final se ordena por fecha para proporcionar una vista cronológica de los me gusta dados durante el período especificado.

Resultados de ejemplo

date likes_count
2023-12-08 123
2023-12-09 156
2023-12-10 278
2023-12-11 134
2023-12-12 89
2 Me gusta

¿Este necesita un AND pa.deleted_at IS NULL para filtrar los Likes eliminados y luego eliminarlos para que coincidan, o sería ese un posible cambio en la consulta del panel?

2 Me gusta

Tal como está actualmente, el informe del panel sí incluye los “me gusta” eliminados, por lo que añadir AND pa.deleted IS NULL cambiaría la forma en que esta consulta coincide con el informe del panel.

Sin embargo, modificar el informe subyacente para que no incluya los “me gusta” eliminados podría ser un buen cambio a considerar para el informe.

2 Me gusta

Mi foro no es muy grande y la mayoría de nuestras reacciones de “me gusta” provienen del “personal” (administradores, moderadores, TL=4). Quería ver cómo se comparaban los “me gusta” de los usuarios habituales frente a los del “personal”, y listar el número de publicaciones por día para tener una mejor idea de lo que está sucediendo y dónde necesitamos concentrar los esfuerzos para mejorar el uso de las reacciones.

Mi amigo ChatGPT y yo ideamos esto:

-- [params]
-- date :start_date = 2024-01-01
-- date :end_date = 2024-12-31

WITH date_range AS (
  SELECT date_trunc('day', series) AS date
  FROM generate_series(
    :start_date::timestamp,
    :end_date::timestamp,
    '1 day'::interval
  ) series
),
staff_users AS (
  SELECT id
  FROM users
  WHERE admin = true OR moderator = true OR trust_level = 4
)

SELECT
  dr.date::date,
  COALESCE(pa_non_staff.regular_likes_count, 0) AS regular_likes_count,
  COALESCE(pa_staff.staff_likes_count, 0) AS staff_likes_count,
  COALESCE(pa_non_staff.regular_likes_count, 0) + COALESCE(pa_staff.staff_likes_count, 0) AS total_likes,
  COALESCE(posts_count.posts_per_day, 0) AS posts_per_day
FROM
  date_range dr
LEFT JOIN (
  SELECT
    date_trunc('day', pa.created_at) AS action_date,
    COUNT(*) AS regular_likes_count
  FROM post_actions pa
  WHERE pa.post_action_type_id = 2
    AND pa.user_id NOT IN (SELECT id FROM staff_users)
    AND pa.created_at >= :start_date
    AND pa.created_at <= (:end_date::date + 1)
  GROUP BY action_date
) pa_non_staff ON dr.date = pa_non_staff.action_date
LEFT JOIN (
  SELECT
    date_trunc('day', pa.created_at) AS action_date,
    COUNT(*) AS staff_likes_count
  FROM post_actions pa
  WHERE pa.post_action_type_id = 2
    AND pa.user_id IN (SELECT id FROM staff_users)
    AND pa.created_at >= :start_date
    AND pa.created_at <= (:end_date::date + 1)
  GROUP BY action_date
) pa_staff ON dr.date = pa_staff.action_date
LEFT JOIN (
  SELECT
    date_trunc('day', p.created_at) AS post_date,
    COUNT(*) AS posts_per_day
  FROM posts p
  WHERE p.created_at >= :start_date
    AND p.created_at <= (:end_date::date + 1)
  GROUP BY post_date
) posts_count ON dr.date = posts_count.post_date
ORDER BY dr.date

Cambios en la consulta original de @SaraDev (¡gracias, Sara!):
Resumen de cambios en SQL

  1. Generar grupo de personal:
    Se agregó una CTE staff_users para identificar a los usuarios del personal de la tabla users. Un usuario del personal se define como cualquiera de:
  • admin = true
  • moderator = true
  • trust_level = 4
  1. Separar “me gusta” del personal:
    Se agregó una subconsulta para calcular el recuento de “me gusta” de los usuarios del personal (staff_likes_count) filtrando post_actions por user_id en el grupo staff_users.
  2. Renombrar columna de “me gusta” de no personal:
    Se cambió la etiqueta de salida para los “me gusta” de no personal de likes_count a regular_likes_count.
  3. Agregar “me gusta” totales:
    Se introdujo una columna total_likes para sumar regular_likes_count y staff_likes_count.
  4. Agregar publicaciones por día:
    Se agregó una subconsulta para calcular el número de publicaciones por día (posts_per_day) y se unió al rango de fechas.
    (Sí, ChatGPT también me hizo esta lista de cambios.)

Resultados de ejemplo:
|fecha|regular_likes_count|staff_likes_count|posts_per_day|
|:—:|:—:|:—:|:—:|\n|1/1/24|0|6|7|\n|1/2/24|0|5|3|\n|1/3/24|1|0|4|\n|1/4/24|1|2|5|\n|1/5/24|9|9|30|\n|1/6/24|0|1|11|\n|1/7/24|2|4|11|\n|1/8/24|0|5|18|\n|1/9/24|0|0|2|\n|1/10/24|0|0|7|\n|1/11/24|0|4|5|\n|1/12/24|4|0|4|\n|1/13/24|6|0|10|\n|1/14/24|1|7|18|\n|1/15/24|2|4|7|

La misma consulta reportada por semanas para suavizar
-- [params]
-- integer :weeks_ago = 52

WITH date_range AS (
  SELECT date_trunc('week', series) AS week_start
  FROM generate_series(
    date_trunc('week', now()) - (:weeks_ago || ' weeks')::interval,
    date_trunc('week', now()),
    '1 week'::interval
  ) series
),
staff_users AS (
  SELECT id
  FROM users
  WHERE admin = true OR moderator = true OR trust_level = 4
)

SELECT
  dr.week_start::date AS week_start,
  COALESCE(pa_non_staff.regular_likes_count, 0) AS regular_likes_count,
  COALESCE(pa_staff.staff_likes_count, 0) AS staff_likes_count,
  COALESCE(pa_non_staff.regular_likes_count, 0) + COALESCE(pa_staff.staff_likes_count, 0) AS total_likes,
  COALESCE(posts_count.posts_per_week, 0) AS posts_per_week
FROM
  date_range dr
LEFT JOIN (
  SELECT
    date_trunc('week', pa.created_at) AS action_week,
    COUNT(*) AS regular_likes_count
  FROM post_actions pa
  WHERE pa.post_action_type_id = 2
    AND pa.user_id NOT IN (SELECT id FROM staff_users)
    AND pa.created_at >= date_trunc('week', now()) - (:weeks_ago || ' weeks')::interval
    AND pa.created_at <= date_trunc('week', now())
  GROUP BY action_week
) pa_non_staff ON dr.week_start = pa_non_staff.action_week
LEFT JOIN (
  SELECT
    date_trunc('week', pa.created_at) AS action_week,
    COUNT(*) AS staff_likes_count
  FROM post_actions pa
  WHERE pa.post_action_type_id = 2
    AND pa.user_id IN (SELECT id FROM staff_users)
    AND pa.created_at >= date_trunc('week', now()) - (:weeks_ago || ' weeks')::interval
    AND pa.created_at <= date_trunc('week', now())
  GROUP BY action_week
) pa_staff ON dr.week_start = pa_staff.action_week
LEFT JOIN (
  SELECT
    date_trunc('week', p.created_at) AS post_week,
    COUNT(*) AS posts_per_week
  FROM posts p
  WHERE p.created_at >= date_trunc('week', now()) - (:weeks_ago || ' weeks')::interval
    AND p.created_at <= date_trunc('week', now())
  GROUP BY post_week
) posts_count ON dr.week_start = posts_count.post_week
ORDER BY dr.week_start

En caso de que sea interesante, aquí están las indicaciones finales que modificaron la consulta de Sara:

Tengo una consulta SQL que reporta el recuento diario de “me gusta” (likes_count) entre dos fechas, pero necesito hacer las siguientes mejoras para producir un resultado final que agrupe los datos por semanas e incluya detalles adicionales:

  1. Definir un grupo de personal:
  • Crear un grupo staff_users a partir de la tabla users. Un usuario se considerará personal si cumple alguno de los siguientes criterios:
    • admin = true
    • moderator = true
    • trust_level = 4
  1. Separar “me gusta” por personal y no personal:
  • Agregar dos columnas separadas:
    • regular_likes_count: Cuenta los “me gusta” de usuarios no personal.
    • staff_likes_count: Cuenta los “me gusta” de usuarios del personal.
  • Asegurarse de que la columna regular_likes_count excluya los “me gusta” generados por usuarios del personal.
  1. Agregar “me gusta” totales:
  • Incluir una columna total_likes que sume regular_likes_count y staff_likes_count.
  1. Agregar publicaciones por período:
  • Agregar una columna posts_per_week que cuente el número de publicaciones creadas durante cada semana.
  1. Agrupar por semanas:
  • Modificar la consulta para agrupar todos los datos por intervalos semanales en lugar de diarios.
  • Incluir una columna week_start que represente la fecha de inicio de cada semana.
  1. Limitar por semanas anteriores:
  • Introducir un parámetro :weeks_ago para limitar los resultados a las últimas N semanas. El valor predeterminado debe ser 52 semanas (1 año).
  1. Ordenar y columnas finales:
  • Asegurarse de que la salida esté ordenada por week_start y que incluya las siguientes columnas en este orden:
    1. week_start: La fecha de inicio de la semana.
    2. regular_likes_count: El recuento de “me gusta” de usuarios no personal.
    3. staff_likes_count: El recuento de “me gusta” de usuarios del personal.
    4. total_likes: La suma de regular_likes_count y staff_likes_count.
    5. posts_per_week: El número de publicaciones creadas durante la semana.
2 Me gusta