Time to first response by group members

Time to first group response for topics created within a given time period

Returns the time to first response by a member of a given group to “regular” (not Personal Message) topics created by a user who is not a member of the given group. The query’s :group_name parameter is set to “staff” by default. With that value, it will give the time to first response by staff members. You can change the value of that parameter to get response times for different groups. For example “customer_support”.

Note that dates should technically be supplied in the form yyyy-mm-dd, but the query would also accept dates in the form dd-mm-yyyy.

-- [params]
-- date :start_date
-- date :end_date
-- string :group_name = staff

WITH group_response_times AS (
    SELECT
        t.category_id,
        t.id AS topic_id,
        EXTRACT(EPOCH FROM (p.created_at - t.created_at)) / 60 AS response_time_minutes,
        p.user_id AS staff_user_id,
        ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY p.created_at) AS row_num
    FROM posts p
    JOIN topics t ON t.id = p.topic_id
    WHERE t.user_id NOT IN (SELECT user_id
                                FROM group_users gu JOIN groups g ON g.id = gu.group_id
                                WHERE gu.user_id > 0 AND g.name = :group_name)
        AND p.user_id IN (SELECT user_id
                              FROM group_users gu JOIN groups g ON g.id = gu.group_id
                              WHERE gu.user_id > 0 AND g.name = :group_name)
        AND t.archetype = 'regular'
        AND t.deleted_at IS NULL
        AND p.post_type = 1
        AND p.deleted_at IS NULL
        AND t.created_at BETWEEN :start_date AND :end_date
)

SELECT
    category_id,
    topic_id,
    staff_user_id,
    ROUND(response_time_minutes::numeric, 2) AS response_time_minutes
FROM group_response_times
WHERE row_num = 1
ORDER BY category_id, response_time_minutes

Average time to first group response per category:

Uses the same logic as the previous query, but returns the average time to the first response by members of the given group per category for topics created by users who are not members of the given group within the time period set by the :start_date and :end_date parameters. As with the previous query, if the :group_name parameter is left at its default value of “staff”, it will return the average staff first response times for regular topics created by non-staff users.

-- [params]
-- date :start_date
-- date :end_date
-- string :group_name = staff

WITH group_response_times AS (
    SELECT
        t.category_id,
        t.id AS topic_id,
        EXTRACT(EPOCH FROM (p.created_at - t.created_at)) / 60 AS response_time_minutes,
        p.user_id AS staff_user_id,
        ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY p.created_at) AS row_num
    FROM posts p
    JOIN topics t ON t.id = p.topic_id
    WHERE t.user_id NOT IN (SELECT user_id
                                FROM group_users gu JOIN groups g ON g.id = gu.group_id
                                WHERE gu.user_id > 0 AND g.name = :group_name)
        AND p.user_id IN (SELECT user_id
                              FROM group_users gu JOIN groups g ON g.id = gu.group_id
                              WHERE gu.user_id > 0 AND g.name = :group_name)
        AND t.archetype = 'regular'
        AND t.deleted_at IS NULL
        AND p.post_type = 1
        AND p.deleted_at IS NULL
        AND t.created_at BETWEEN :start_date AND :end_date
)

SELECT
    category_id,
    ROUND(AVG (response_time_minutes)::numeric, 2) AS average_response_time_minutes,
    COUNT(*) AS num_topics_with_staff_responses
FROM group_response_times
WHERE row_num = 1
GROUP BY category_id

Hola @simon

Gracias por compartir una posible solución para generar informes sobre el tiempo de respuesta del personal mensualmente. ¿Sigue siendo viable este código o hay una versión actualizada? Además, ¿hay alguna forma de generar este informe semanalmente en lugar de mensualmente? Muchas gracias.

1 me gusta

No lo es. Sinceramente, no sé cómo funcionó alguna vez. Publicaré una versión actualizada pronto y te avisaré cuando esté lista.

Editar: @IreneT
Aquí tienes una versión corregida de la consulta original. Ignoraría esa consulta y miraría las otras consultas que he publicado en esta respuesta. Hazme saber si tienes alguna pregunta sobre las consultas o si tienes algún problema al agregar los parámetros requeridos a las consultas. Según las pruebas de hoy, me doy cuenta de que tengo que actualizar la página después de guardar una consulta del Explorador de Datos para que los campos de entrada de parámetros aparezcan debajo de la consulta. (Eso puede ser solo una peculiaridad en mi sitio de desarrollo local).

-- [params]
-- int :months_ago = 1

WITH query_period AS (
SELECT
date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' as period_start,
date_trunc('month', CURRENT_DATE) - INTERVAL ':months_ago months' + INTERVAL '1 month' - INTERVAL '1 second' as period_end
),
staff_responses AS (
SELECT
DISTINCT ON (p.topic_id)
p.topic_id,
p.created_at,
t.category_id,
EXTRACT(MINUTE FROM (p.created_at - t.created_at)) AS response_time
FROM posts p
JOIN topics t
ON t.id = p.topic_id
AND t.category_id = ANY ('{46,25,43,40,44,35,22,7,20,17,6,12}'::int[])
JOIN users u
ON u.id = p.user_id
WHERE p.post_number > 1
AND u.admin = 't' OR u.moderator = 't'
ORDER BY p.topic_id, p.created_at
),
user_topics AS (
SELECT
t.id
FROM topics t
JOIN users u
ON u.id = t.user_id
WHERE u.admin = 'f' AND u.moderator = 'f'
)

SELECT
sr.category_id,
AVG(sr.response_time) AS "Average First Response Time",
COUNT(1) AS "Topics Responded to"
FROM staff_responses sr
JOIN query_period qp
ON sr.created_at >= qp.period_start
AND sr.created_at <= qp.period_end
JOIN user_topics t
ON t.id = sr.topic_id
GROUP BY sr.category_id

Lo que se cambió fue:

--DATE_TRUNC('minute', p.created_at - t.created_at) AS response_time
EXTRACT(MINUTE FROM (p.created_at - t.created_at)) AS response_time

Me sorprendería si escribí la consulta antigua sin probarla. En cualquier caso, la versión actualizada funciona como se esperaba ahora.

Según recuerdo, esta consulta se escribió para un sitio específico y no estaba destinada a publicarse en Meta. Aquí hay un par de consultas más útiles para obtener información sobre los tiempos de respuesta del personal:

Tiempo de la primera respuesta grupal para temas creados dentro de un período de tiempo determinado

Devuelve el tiempo de la primera respuesta de un miembro de un grupo determinado a temas “regulares” (no Mensajes Personales) creados por un usuario que no es miembro del grupo determinado. El parámetro :group_name de la consulta se establece en “staff” por defecto. Con ese valor, dará el tiempo de la primera respuesta de los miembros del personal. Puedes cambiar el valor de ese parámetro para obtener los tiempos de respuesta de diferentes grupos. Por ejemplo, “customer_support”.

Tenga en cuenta que las fechas deben proporcionarse técnicamente en el formato aaaa-mm-dd, pero la consulta también aceptaría fechas en el formato dd-mm-aaaa.

-- [params]
-- date :start_date
-- date :end_date
-- string :group_name = staff

WITH group_response_times AS (
    SELECT
        t.category_id,
        t.id AS topic_id,
        EXTRACT(MINUTE FROM (p.created_at - t.created_at)) AS response_time_minutes,
        p.user_id AS staff_user_id,
        ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY p.created_at) AS row_num
    FROM posts p
    JOIN topics t ON t.id = p.topic_id
    WHERE t.user_id NOT IN (SELECT user_id
                                FROM group_users gu JOIN groups g ON g.id = gu.group_id
                                WHERE gu.user_id > 0 AND g.name = :group_name)
        AND p.user_id IN (SELECT user_id
                              FROM group_users gu JOIN groups g ON g.id = gu.group_id
                              WHERE gu.user_id > 0 AND g.name = :group_name)
        AND t.archetype = 'regular'
        AND t.deleted_at IS NULL
        AND p.post_type = 1
        AND p.deleted_at IS NULL
        AND t.created_at BETWEEN :start_date AND :end_date
)

SELECT
    category_id,
    topic_id,
    staff_user_id,
    response_time_minutes
FROM group_response_times
WHERE row_num = 1
ORDER BY category_id, response_time_minutes

Tiempo promedio de la primera respuesta grupal por categoría:

Utiliza la misma lógica que la consulta anterior, pero devuelve el tiempo promedio de la primera respuesta de los miembros del grupo determinado por categoría para los temas creados por usuarios que no son miembros del grupo determinado dentro del período de tiempo establecido por los parámetros :start_date y :end_date. Al igual que con la consulta anterior, si el parámetro :group_name se deja en su valor predeterminado “staff”, devolverá los tiempos promedio de la primera respuesta del personal para temas regulares creados por usuarios que no son del personal.

-- [params]
-- date :start_date
-- date :end_date
-- string :group_name = staff

WITH group_response_times AS (
    SELECT
        t.category_id,
        t.id AS topic_id,
        EXTRACT(MINUTE FROM (p.created_at - t.created_at)) AS response_time_minutes,
        p.user_id AS staff_user_id,
        ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY p.created_at) AS row_num
    FROM posts p
    JOIN topics t ON t.id = p.topic_id
    WHERE t.user_id NOT IN (SELECT user_id
                                FROM group_users gu JOIN groups g ON g.id = gu.group_id
                                WHERE gu.user_id > 0 AND g.name = :group_name)
        AND p.user_id IN (SELECT user_id
                              FROM group_users gu JOIN groups g ON g.id = gu.group_id
                              WHERE gu.user_id > 0 AND g.name = :group_name)
        AND t.archetype = 'regular'
        AND t.deleted_at IS NULL
        AND p.post_type = 1
        AND p.deleted_at IS NULL
        AND t.created_at BETWEEN :start_date AND :end_date
)

SELECT
    category_id,
    AVG (response_time_minutes) AS average_response_time_minutes,
    COUNT(*) AS num_topics_with_staff_responses
FROM group_response_times
WHERE row_num = 1
GROUP BY category_id

@JammyDodger, tal vez reemplace la consulta en el OP con las dos últimas consultas en esta publicación. Además, probablemente actualice el título a algo como “Tiempo de la primera respuesta por miembros del grupo”.

3 Me gusta

@simon Tengo muchas ganas.
Muchas gracias.

1 me gusta

No, desafortunadamente esto es algo conocido. Sin embargo, una actualización rápida soluciona el problema. :+1:

4 Me gusta

Esto es perfecto, @simon. Agradezco mucho tu ayuda con esto. Solo un pequeño ajuste de nuestra parte para tener los datos que necesitamos, pero creo que estamos bien. Muchas gracias :heart:

1 me gusta

Hola @simon

Hemos instalado Data Explorer y hemos utilizado los códigos que compartiste. Lo que queremos específicamente es tener el Tiempo de primera respuesta por el nombre de cada miembro del grupo. Sin embargo, la consulta está enviando los tiempos de respuesta agrupados por el ID de la categoría y no por el ID del usuario.

Agradeceríamos tu ayuda. Gracias.

Gracias por plantearlo, porque hay un error en las consultas que publiqué:

EXTRACT(MINUTE FROM (p.created_at - t.created_at)) AS response_time_minutes

Esa línea está haciendo exactamente lo que dice, extrayendo minutos de las marcas de tiempo. Eso significa que si un tema se creó a las 12:00 y se respondió un mes después a las 12:05, la consulta calculaba un tiempo de respuesta de 5 minutos.

La solución es:

EXTRACT(EPOCH FROM (p.created_at - t.created_at))/ 60 AS response_time_minutes

Las fechas pueden ser complicadas. No puedo editar la OP, así que publicaré las correcciones aquí, y luego la respuesta a tu pregunta en una publicación separada.

@JammyDodger, aquí tienes nuevas versiones de las 2 consultas en la OP:

Tiempo de primera respuesta por tema para miembros de un grupo

-- [params]
-- date :start_date
-- date :end_date
-- string :group_name = staff

WITH group_response_times AS (
    SELECT
        t.category_id,
        t.id AS topic_id,
        EXTRACT(EPOCH FROM (p.created_at - t.created_at)) / 60 AS response_time_minutes,
        p.user_id AS staff_user_id,
        ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY p.created_at) AS row_num
    FROM posts p
    JOIN topics t ON t.id = p.topic_id
    WHERE t.user_id NOT IN (SELECT user_id
                                FROM group_users gu JOIN groups g ON g.id = gu.group_id
                                WHERE gu.user_id > 0 AND g.name = :group_name)
        AND p.user_id IN (SELECT user_id
                              FROM group_users gu JOIN groups g ON g.id = gu.group_id
                              WHERE gu.user_id > 0 AND g.name = :group_name)
        AND t.archetype = 'regular'
        AND t.deleted_at IS NULL
        AND p.post_type = 1
        AND p.deleted_at IS NULL
        AND t.created_at BETWEEN :start_date AND :end_date
)

SELECT
    category_id,
    topic_id,
    staff_user_id,
    ROUND(response_time_minutes::numeric, 2) AS response_time_minutes
FROM group_response_times
WHERE row_num = 1
ORDER BY category_id, response_time_minutes

Tiempo promedio de primera respuesta grupal por categoría

-- [params]
-- date :start_date
-- date :end_date
-- string :group_name = staff

WITH group_response_times AS (
    SELECT
        t.category_id,
        t.id AS topic_id,
        EXTRACT(EPOCH FROM (p.created_at - t.created_at)) / 60 AS response_time_minutes,
        p.user_id AS staff_user_id,
        ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY p.created_at) AS row_num
    FROM posts p
    JOIN topics t ON t.id = p.topic_id
    WHERE t.user_id NOT IN (SELECT user_id
                                FROM group_users gu JOIN groups g ON g.id = gu.group_id
                                WHERE gu.user_id > 0 AND g.name = :group_name)
        AND p.user_id IN (SELECT user_id
                              FROM group_users gu JOIN groups g ON g.id = gu.group_id
                              WHERE gu.user_id > 0 AND g.name = :group_name)
        AND t.archetype = 'regular'
        AND t.deleted_at IS NULL
        AND p.post_type = 1
        AND p.deleted_at IS NULL
        AND t.created_at BETWEEN :start_date AND :end_date
)

SELECT
    category_id,
    ROUND(AVG (response_time_minutes)::numeric, 2) AS average_response_time_minutes,
    COUNT(*) AS num_topics_with_staff_responses
FROM group_response_times
WHERE row_num = 1
GROUP BY category_id
4 Me gusta

Supongo que quieres el tiempo promedio de primera respuesta por miembro del grupo. Si solo quieres el tiempo que tardaron los miembros del grupo en responder a temas individuales, usa la versión corregida de la primera consulta en el OP:

-- [params]
-- date :start_date
-- date :end_date
-- string :group_name = staff

WITH group_response_times AS (
    SELECT
        t.category_id,
        t.id AS topic_id,
        EXTRACT(EPOCH FROM (p.created_at - t.created_at))/ 60 AS response_time_minutes,
        p.user_id AS staff_user_id,
        ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY p.created_at) AS row_num
    FROM posts p
    JOIN topics t ON t.id = p.topic_id
    WHERE t.user_id NOT IN (SELECT user_id
                                FROM group_users gu JOIN groups g ON g.id = gu.group_id
                                WHERE gu.user_id > 0 AND g.name = :group_name)
        AND p.user_id IN (SELECT user_id
                              FROM group_users gu JOIN groups g ON g.id = gu.group_id
                              WHERE gu.user_id > 0 AND g.name = :group_name)
        AND t.archetype = 'regular'
        AND t.deleted_at IS NULL
        AND p.post_type = 1
        AND p.deleted_at IS NULL
        AND t.created_at BETWEEN :start_date AND :end_date
)

SELECT
    category_id,
    topic_id,
    staff_user_id,
    ROUND(response_time_minutes::numeric, 2) AS response_time_minutes
FROM group_response_times
WHERE row_num = 1
ORDER BY category_id, response_time_minutes

Eso te dirá el tiempo que tardaron los miembros del grupo en responder a temas individuales. Los resultados están organizados por categoría, pero la categoría puede ser ignorada.

No estoy seguro de cuán significativos serán los datos de los tiempos promedio de respuesta para los miembros individuales del grupo. Sería prudente no usarlos para ningún tipo de revisión de desempeño, ya que podría penalizar a los miembros del grupo que responden a preguntas o temas difíciles que otros miembros del grupo han ignorado. Teniendo esto en cuenta, aquí tienes una consulta que devuelve los tiempos promedio de respuesta para los miembros del grupo y el número de temas a los que han sido el primer miembro del grupo en responder:

-- [params]
-- date :start_date
-- date :end_date
-- string :group_name = staff

WITH group_response_times AS (
    SELECT
        t.category_id,
        t.id AS topic_id,
        EXTRACT(EPOCH FROM (p.created_at - t.created_at))/ 60 AS response_time_minutes,
        p.user_id AS staff_user_id,
        ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY p.created_at) AS row_num
    FROM posts p
    JOIN topics t ON t.id = p.topic_id
    WHERE t.user_id NOT IN (SELECT user_id
                                FROM group_users gu JOIN groups g ON g.id = gu.group_id
                                WHERE gu.user_id > 0 AND g.name = :group_name)
        AND p.user_id IN (SELECT user_id
                              FROM group_users gu JOIN groups g ON g.id = gu.group_id
                              WHERE gu.user_id > 0 AND g.name = :group_name)
        AND t.archetype = 'regular'
        AND t.deleted_at IS NULL
        AND p.post_type = 1
        AND p.deleted_at IS NULL
        AND t.created_at BETWEEN :start_date AND :end_date
)

SELECT
    staff_user_id,
    ROUND(AVG(response_time_minutes)::numeric, 2) AS average_response_time_minutes,
    COUNT(*) AS number_of_topics_responded_to
FROM group_response_times
WHERE row_num = 1
GROUP BY staff_user_id
ORDER BY average_response_time_minutes
2 Me gusta

Gracias @simon, respuesta muy útil y rápida como siempre. ¡Realmente lo aprecio!

En realidad, nuestro objetivo es que nuestros entrenadores (añadidos en uno de los grupos) midan su tiempo medio de respuesta. De esta manera, tendremos una referencia de lo bien que lo estamos haciendo en cuanto a la prestación de apoyo a la comunidad en el discurso y a garantizar la satisfacción de los miembros a largo plazo.

Tiene sentido. Hay muchos datos que podrían ser interesantes o útiles si se interpretan de la manera correcta. Con las consultas del Explorador de Datos, a veces me preocupa poder escribir consultas que se utilicen para generar informes de rendimiento.

He realizado mucho trabajo de atención al cliente. Creo que el tiempo de respuesta inicial es una métrica útil, pero también hay que tener en cuenta la calidad de la respuesta.

Una cosa que las consultas de este tema no te dicen es nada sobre los temas que no han recibido respuesta de un miembro del grupo. Las consultas solo devuelven datos sobre los temas a los que se ha respondido. No estoy seguro de cuál es la mejor manera de añadir información sobre los temas sin respuesta a las consultas.

@simon tiene sentido.

Gracias por compartir esa información. Quizás esta métrica (tiempo de primera respuesta) siempre debería estar respaldada por el otro informe sobre “Temas sin respuesta”.

¿Es posible que cuando se haga clic en un valor en este informe, sea un enlace para mostrar un resumen de esos “temas que no tienen respuesta”? De esa manera, podemos verificar si podemos responder a todos los temas que necesitan respuestas dentro de un tiempo especificado y eso excluiría aquellos temas que realmente no necesitan respuesta en absoluto.

No es posible. La mejor opción para obtener enlaces a temas sin respuesta podría ser usar una consulta de Data Explorer. Podría haber una consulta de ejemplo para eso en Meta, pero no encuentro ninguna al buscarla.

Otra opción es instalar el componente temático Unanswered Filter: Unanswered Filter. Agrega un elemento desplegable al menú de navegación del sitio que te permite filtrar las listas de temas para mostrar solo los temas sin respuesta.

Muchas gracias @simon. Agradezco como siempre :heart:

1 me gusta