Tempo para a primeira resposta dos membros do grupo

Tempo até a primeira resposta do grupo para tópicos criados dentro de um período específico

Retorna o tempo até a primeira resposta de um membro de um determinado grupo para tópicos “comuns” (não Mensagens Privadas) criados por um usuário que não é membro do grupo especificado. O parâmetro :group_name da consulta é definido como “staff” por padrão. Com esse valor, ela retornará o tempo até a primeira resposta por membros da equipe. Você pode alterar o valor desse parâmetro para obter tempos de resposta de diferentes grupos. Por exemplo, “customer_support”.

Observe que, tecnicamente, as datas devem ser fornecidas no formato aaaa-mm-dd, mas a consulta também aceitará datas no 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(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

Tempo médio até a primeira resposta do grupo por categoria:

Utiliza a mesma lógica da consulta anterior, mas retorna o tempo médio até a primeira resposta por membros do grupo especificado, por categoria, para tópicos criados por usuários que não são membros do grupo dentro do período definido pelos parâmetros :start_date e :end_date. Assim como na consulta anterior, se o parâmetro :group_name for mantido com seu valor padrão de “staff”, ela retornará os tempos médios de primeira resposta da equipe para tópicos comuns criados por usuários que não são da equipe.

-- [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

Olá @simon

Obrigado por compartilhar uma possível solução para gerar relatórios de tempo de resposta da equipe mensalmente. Este código ainda é viável ou existe uma versão atualizada? Além disso, há alguma maneira de gerar este relatório semanalmente em vez de mensalmente? Muito obrigado.

Não é. Honestamente, não tenho certeza de como já funcionou. Postarei uma versão atualizada em breve e pingarei você para avisar que está pronta.

Editar: @IreneT
Aqui está uma versão corrigida da consulta original. Eu ignoraria essa consulta e olharia as outras consultas que postei nesta resposta. Me avise se você tiver alguma dúvida sobre as consultas ou se tiver problemas para adicionar os parâmetros necessários às consultas. Com base nos testes de hoje, estou descobrindo que preciso atualizar a página depois de salvar uma consulta do Data Explorer para que os campos de entrada de parâmetros apareçam abaixo da consulta. (Isso pode ser apenas uma peculiaridade no meu site de desenvolvimento 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 "Tempo Médio de Primeira Resposta",
COUNT(1) AS "Tópicos Respondidos"
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

O que foi alterado foi:

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

Ficaria surpreso se eu tivesse escrito a consulta antiga sem testá-la. De qualquer forma, a versão atualizada funciona como esperado agora.

De memória, esta consulta foi escrita para um site específico e não se destinava a ser postada no Meta. Aqui estão mais algumas consultas úteis para obter informações sobre os tempos de resposta da equipe:

Tempo para a primeira resposta do grupo para tópicos criados em um determinado período:

Retorna o tempo para a primeira resposta de um membro de um determinado grupo para tópicos “regulares” (não Mensagem Pessoal) criados por um usuário que não é membro do determinado grupo. O parâmetro :group_name da consulta está definido como “staff” por padrão. Com esse valor, ele dará o tempo para a primeira resposta dos membros da equipe. Você pode alterar o valor desse parâmetro para obter tempos de resposta para diferentes grupos. Por exemplo, “customer_support”.

Observe que as datas devem ser fornecidas tecnicamente no formato aaaa-mm-dd, mas a consulta também aceitaria datas no 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

Tempo médio para a primeira resposta do grupo por categoria:

Usa a mesma lógica da consulta anterior, mas retorna o tempo médio para a primeira resposta de membros do grupo especificado por categoria para tópicos criados por usuários que não são membros do grupo especificado dentro do período de tempo definido pelos parâmetros :start_date e :end_date. Assim como na consulta anterior, se o parâmetro :group_name for deixado em seu valor padrão “staff”, ele retornará os tempos médios de primeira resposta da equipe para tópicos regulares criados por usuários não pertencentes à equipe.

-- [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, talvez substitua a consulta na OP pelas duas últimas consultas neste post. Além disso, provavelmente atualize o título para algo como “Tempo para a primeira resposta por membros do grupo”.

@simon Ansioso por isso.

Muito apreciado. Obrigado

Não, infelizmente isso é algo conhecido. Uma atualização rápida resolve o problema. :+1:

Isso está perfeito, @simon. Agradeço muito sua ajuda com isso. Apenas um pequeno ajuste da nossa parte para termos os dados de que precisamos, mas acho que estamos bem. Muito obrigado :heart:

Olá @simon

Nós instalamos o Data Explorer e usamos os códigos que você compartilhou. O que queremos especificamente é ter o Tempo de primeira resposta pelo nome de cada membro do grupo. No entanto, a consulta está enviando os tempos de resposta agrupados pelo ID da categoria e não pelo ID do usuário.

Agradeceríamos sua ajuda. Obrigado.

Obrigado por levantar isso, porque há um erro nas consultas que postei:

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

Essa linha está fazendo exatamente o que diz, extraindo minutos dos timestamps. Isso significa que se um tópico foi criado às 12:00, e respondido um mês depois às 12:05, a consulta estava calculando um tempo de resposta de 5 minutos.

A correção é:

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

Datas podem ser complicadas. Não posso editar o OP, então postarei as correções aqui, e depois a resposta para sua pergunta em uma postagem separada.

@JammyDodger, aqui estão as novas versões das 2 consultas no OP:

Tempo para a primeira resposta por tópico para membros de um 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

Tempo médio para a primeira resposta do grupo por categoria

-- [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

Presumo que você queira o tempo médio de primeira resposta por membro do grupo. Se você quiser apenas o tempo que os membros do grupo levaram para responder a tópicos individuais, use a versão corrigida da primeira consulta na 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

Isso informará o tempo que os membros do grupo levaram para responder a tópicos individuais. Os resultados são organizados por categoria, mas a categoria pode ser ignorada.

Não tenho certeza de quão significativos seriam os dados para tempos médios de resposta para membros individuais do grupo. Eu teria cuidado ao usá-los para qualquer tipo de avaliação de desempenho, pois pode penalizar membros do grupo que respondem a perguntas difíceis ou tópicos que outros membros do grupo ignoraram. Com isso em mente, aqui está uma consulta que retorna os tempos médios de resposta para membros do grupo e o número de tópicos aos quais eles foram o primeiro membro do grupo a 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

Obrigado @simon - resposta muito útil e rápida como sempre. Realmente aprecio!

Na verdade, nosso objetivo é que nossos treinadores (adicionados em um dos grupos) meçam seu tempo médio de resposta. Dessa forma, teremos uma referência de quão bem estamos nos saindo em relação ao fornecimento de suporte comunitário em discussões e à garantia de membros satisfeitos a longo prazo.

Isso faz sentido. Há muitos dados que podem ser interessantes ou úteis se interpretados da maneira certa. Com as consultas do Explorador de Dados, às vezes me preocupo que eu possa estar escrevendo consultas que são usadas para gerar relatórios de desempenho.

Eu fiz muito trabalho de suporte ao cliente. Acho que o tempo para a primeira resposta é uma métrica útil, mas a qualidade da resposta também precisa ser levada em consideração.

Uma coisa que as consultas neste tópico não estão dizendo é sobre tópicos que não receberam resposta de um membro do grupo. As consultas estão retornando apenas dados sobre tópicos que foram respondidos. Não tenho certeza de como melhor adicionar informações sobre tópicos sem resposta às consultas.

@simon faz sentido.

Obrigado por compartilhar essa informação. Talvez essa métrica (tempo para a primeira resposta) deva ser sempre apoiada pelo outro relatório sobre “Tópicos sem resposta”.

É possível que, quando um valor for clicado neste relatório, ele seja um link para mostrar um resumo desses “tópicos que não têm resposta”? Dessa forma, podemos verificar se somos capazes de responder a todos os tópicos que precisam de respostas dentro de um tempo especificado e isso excluiria os tópicos que realmente não precisam de resposta alguma.

Não é. A melhor opção para obter links para tópicos sem resposta pode ser usar uma consulta do Data Explorer. Pode haver um exemplo de consulta para isso no Meta, mas não estou encontrando um ao pesquisar por ele.

Outra opção é instalar o componente de tema Unanswered Filter: Unanswered Filter. Ele adiciona um item suspenso ao menu de navegação do site que permite filtrar listas de tópicos para exibir apenas tópicos sem resposta.

Muito obrigado @simon . Agradeço como sempre :heart: