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

こんにちは、@simon さん

スタッフの月次応答時間に関するレポートを生成するための潜在的なソリューションを共有していただきありがとうございます。このコードはまだ有効ですか、それとも更新されたバージョンはありますか?また、このレポートを月次ではなく週次で作成する方法はありますか?どうもありがとうございます。

「いいね!」 1

それは使えません。正直なところ、これがどのように機能していたのかさえわかりません。すぐに更新版を投稿し、準備ができたら通知します。

編集:@IreneT
元のクエリの修正版はこちらです。そのクエリは無視して、代わりにこの返信に投稿した他のクエリを見てください。クエリに関する質問がある場合、またはクエリに必要なパラメーターを追加する際に問題が発生した場合は、お知らせください。今日のテストから、Data Explorer クエリを保存した後、パラメーター入力フィールドがクエリの下に表示されるようにページを更新する必要があることがわかりました。(これは私のローカル開発サイトでのみ発生する可能性のある不具合かもしれません。)

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

変更されたのは次のとおりです。

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

古いクエリをテストせずに書いたとしたら驚きです。いずれにせよ、更新されたバージョンは期待どおりに機能します。

記憶によれば、このクエリは特定のサイト用に書かれたもので、Meta に投稿することを意図したものではありませんでした。スタッフの応答時間に関する情報を取得するための、さらに便利なクエリをいくつか紹介します。

指定された期間内に作成されたトピックに対する最初のグループ応答までの時間

指定されたグループのメンバーによる、指定されたグループのメンバーではないユーザーによって作成された「通常」(ダイレクトメッセージではない)トピックへの最初の応答までの時間を返します。クエリの :group_name パラメーターはデフォルトで「staff」に設定されています。この値を使用すると、スタッフメンバーからの最初の応答までの時間が得られます。このパラメーターの値を変更して、異なるグループの応答時間を取得できます。たとえば、「customer_support」などです。

日付は技術的には yyyy-mm-dd の形式で指定する必要がありますが、クエリは 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(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

カテゴリごとの最初のグループ応答までの平均時間:

前のクエリと同じロジックを使用しますが、指定された期間内に指定されたグループのメンバーではないユーザーによって作成されたトピックについて、指定されたグループのメンバーによる最初の応答までの 平均 時間を返します。前のクエリと同様に、:group_name パラメーターがデフォルト値の「staff」のままの場合、非スタッフユーザーによって作成された通常のトピックに対する平均スタッフ応答時間が返されます。

-- [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、OP のクエリをこの投稿の最後の 2 つのクエリに置き換えることを検討してください。また、タイトルを「グループメンバーによる最初の応答までの時間」のようなものに更新すると良いでしょう。

「いいね!」 3

@simon 楽しみにしています。
大変感謝しています。ありがとうございます。

「いいね!」 1

いいえ、残念ながらこれは既知の問題です。ただし、すぐにリロードすれば解決します。:+1:

「いいね!」 4

@simon さん、完璧です。ご協力いただき大変感謝しております。必要なデータを取得するために、こちらで少し調整が必要ですが、これで問題ないかと思います。本当にありがとうございました :heart:

「いいね!」 1

こんにちは、@simon様

Data Explorerをインストールし、共有していただいたコードを使用しました。具体的には、各グループメンバーの名前ごとの最初の応答時間を知りたいのですが、クエリはユーザーIDではなくカテゴリIDで応答時間をグループ化しています。

ご協力いただけると幸いです。ありがとうございます。

ご指摘ありがとうございます。投稿したクエリにエラーがありました。

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

この行は、タイムスタンプから分を抽出しています。つまり、トピックが12:00に作成され、1か月後の12:05に応答があった場合、クエリは5分の応答時間を計算していました。

修正は次のとおりです。

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

日付はトリッキーな場合があります。OPは編集できないため、修正をここに投稿し、質問への回答は別の投稿で投稿します。

@JammyDodger、OPの2つのクエリの新しいバージョンは次のとおりです。

グループメンバーごとのトピックごとの最初の応答時間

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

カテゴリごとの平均最初のグループ応答時間

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

グループメンバーごとの最初の応答時間の平均値を知りたいということでしょうか。もし単に、グループメンバーが個々のトピックに応答するのにかかった時間だけを知りたいのであれば、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

これにより、グループメンバーが個々のトピックに応答するのにかかった時間がわかります。結果はカテゴリ別に整理されていますが、カテゴリは無視できます。

個々のグループメンバーの平均応答時間のデータがどれほど意味のあるものになるかはわかりません。パフォーマンス評価に使用するのは注意が必要です。なぜなら、他のグループメンバーが無視した難しい質問やトピックに応答したグループメンバーに不利益を与える可能性があるからです。それを念頭に置いて、グループメンバーの平均応答時間と、彼らが最初にグループメンバーとして応答したトピックの数を返すクエリを以下に示します。

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

@simonさん、いつもながら迅速で的確なご対応ありがとうございます。大変助かります!

実は、私たちの目的は、コーチ(グループの1つに追加されています)が平均応答時間を測定できるようにすることです。これにより、ディスコースでのコミュニティサポートの提供状況や、長期的なメンバー満足度の維持状況を把握するための基準となります。

なるほど。解釈の仕方次第で、興味深く、または役に立つデータがたくさんあります。Data Explorerクエリを使用する場合、パフォーマンスレポートの生成に使用される可能性のあるクエリを作成してしまうのではないかと心配になることがあります。

私は多くのカスタマーサポート業務を行ってきました。最初の応答時間(time to first response)は有用な指標だと 思います が、応答の質も考慮する必要があります。

このトピックのクエリでは、グループメンバーからの応答が なかった トピックに関する情報は何も教えてくれません。クエリは応答があったトピックのデータのみを返します。応答のないトピックに関する情報をクエリに追加する最善の方法がわかりません。

@simon それは理にかなっています。

その情報を共有していただきありがとうございます。この指標(最初の応答までの時間)は、「応答のないトピック」に関する別のレポートで常に裏付けられるべきでしょうか。

このレポートの値がクリックされたときに、「応答のないトピック」の概要を表示するリンクになる可能性はありますか?そうすれば、指定された時間内に応答が必要なすべてのトピックに対応できているかどうかを確認でき、まったく応答を必要としないトピックを除外できます。

それはできません。返信のないトピックへのリンクを取得する最善の方法は、Data Explorer クエリを使用することかもしれません。Meta にはそれに関する例のクエリがあるかもしれませんが、検索しても見つかりません。

別のオプションは、Unanswered Filter テーマコンポーネントをインストールすることです: https://meta.discourse.org/t/unanswered-filter/126257。これにより、サイトのナビゲーションメニューにドロップダウンアイテムが追加され、トピックリストを返信のないトピックのみを表示するようにフィルタリングできます。

@simon さん、ありがとうございます。いつも感謝しています :heart:

「いいね!」 1