dalongxia/remote_sync/customer_service_experience_report.sql
2026-03-04 13:25:48 +08:00

275 lines
12 KiB
Transact-SQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

USE [JSDataMiddlePlatform];
SET NOCOUNT ON;
DECLARE @start_date date = DATEADD(day, -29, CAST(GETDATE() AS date));
DECLARE @end_date date = CAST(GETDATE() AS date);
DECLARE @start_time datetime2 = CAST(@start_date AS datetime2);
DECLARE @end_time datetime2 = DATEADD(day, 1, CAST(@end_date AS datetime2));
DECLARE @shop nvarchar(100) = N''; -- 可填店铺关键词,例如 N'亿家达'
DECLARE @limit int = 20;
IF OBJECT_ID('tempdb..#chat_base') IS NOT NULL
DROP TABLE #chat_base;
SELECT
c.id,
NULLIF(c.orderNo, '') AS order_no,
ISNULL(NULLIF(c.shopName, ''), N'未知店铺') AS shop_name,
c.createdAt,
c.updatedAt,
c.state,
ISNULL(NULLIF(LTRIM(RTRIM(c.mainReason)), ''), N'未标注') AS main_reason,
ISNULL(NULLIF(LTRIM(RTRIM(c.secondReason)), ''), N'未标注') AS second_reason,
ISNULL(NULLIF(LTRIM(RTRIM(c.thirdReason)), ''), N'未标注') AS third_reason,
CAST(c.content AS nvarchar(max)) AS content_text,
CAST(c.reasonContent AS nvarchar(max)) AS reason_text
INTO #chat_base
FROM dbo.pc_kfChatLog AS c WITH (NOLOCK)
WHERE c.createdAt >= @start_time
AND c.createdAt < @end_time
AND (@shop = N'' OR c.shopName LIKE N'%' + @shop + N'%');
IF OBJECT_ID('tempdb..#chat_norm') IS NOT NULL
DROP TABLE #chat_norm;
SELECT
b.*,
CASE WHEN b.content_text IS NULL OR LTRIM(RTRIM(b.content_text)) IN (N'', N'[]') THEN 1 ELSE 0 END AS is_empty_content,
CASE WHEN b.main_reason = N'未标注' OR b.second_reason = N'未标注' THEN 1 ELSE 0 END AS is_missing_reason,
CASE WHEN b.main_reason = N'无对应分类' OR b.second_reason = N'无对应分类' THEN 1 ELSE 0 END AS is_no_match_reason,
CASE WHEN b.content_text LIKE N'%欢迎%' OR b.content_text LIKE N'%您好%' OR b.content_text LIKE N'%亲,%' OR b.content_text LIKE N'%亲,%' THEN 1 ELSE 0 END AS has_greeting,
CASE WHEN b.content_text LIKE N'%抱歉%' OR b.content_text LIKE N'%不好意思%' OR b.content_text LIKE N'%理解您%' THEN 1 ELSE 0 END AS has_empathy,
CASE WHEN b.content_text LIKE N'%马上%' OR b.content_text LIKE N'%尽快%' OR b.content_text LIKE N'%给您处理%' OR b.content_text LIKE N'%为您处理%' THEN 1 ELSE 0 END AS has_commitment,
CASE WHEN b.content_text LIKE N'%感谢%' OR b.content_text LIKE N'%祝您%' OR b.content_text LIKE N'%辛苦%' THEN 1 ELSE 0 END AS has_closing,
CASE WHEN b.content_text LIKE N'%不知道%' OR b.content_text LIKE N'%没办法%' OR b.content_text LIKE N'%无法处理%' OR b.content_text LIKE N'%不清楚%' OR b.content_text LIKE N'%等不了%' OR b.content_text LIKE N'%太慢%' THEN 1 ELSE 0 END AS has_risk_phrase
INTO #chat_norm
FROM #chat_base AS b;
-- 1) 时间段客服体验总览
SELECT
@start_date AS start_date,
@end_date AS end_date,
COUNT(*) AS session_count,
COUNT(DISTINCT order_no) AS order_count,
COUNT(DISTINCT shop_name) AS shop_count,
SUM(is_empty_content) AS empty_content_count,
CAST(100.0 * SUM(is_empty_content) / NULLIF(COUNT(*), 0) AS decimal(6,2)) AS empty_content_rate_pct,
SUM(is_missing_reason) AS missing_reason_count,
CAST(100.0 * SUM(is_missing_reason) / NULLIF(COUNT(*), 0) AS decimal(6,2)) AS missing_reason_rate_pct,
SUM(is_no_match_reason) AS no_match_reason_count,
CAST(100.0 * SUM(is_no_match_reason) / NULLIF(COUNT(*), 0) AS decimal(6,2)) AS no_match_reason_rate_pct,
SUM(CASE WHEN state = 1 THEN 1 ELSE 0 END) AS state_1_count,
SUM(CASE WHEN state = 2 THEN 1 ELSE 0 END) AS state_2_count
FROM #chat_norm;
-- 2) 用户反馈问题结构 TOP(一级/二级)
SELECT TOP (@limit)
main_reason,
second_reason,
COUNT(*) AS session_count,
COUNT(DISTINCT order_no) AS order_count,
CAST(100.0 * COUNT(*) / NULLIF((SELECT COUNT(*) FROM #chat_norm), 0) AS decimal(6,2)) AS session_share_pct
FROM #chat_norm
GROUP BY main_reason, second_reason
ORDER BY session_count DESC, order_count DESC;
-- 3) 客服体验店铺对比(体验分)
WITH shop_agg AS (
SELECT
shop_name,
COUNT(*) AS session_count,
SUM(is_empty_content) AS empty_content_count,
SUM(is_missing_reason) AS missing_reason_count,
SUM(is_no_match_reason) AS no_match_reason_count
FROM #chat_norm
GROUP BY shop_name
)
SELECT TOP (@limit)
s.shop_name,
s.session_count,
CAST(100.0 * s.empty_content_count / NULLIF(s.session_count, 0) AS decimal(6,2)) AS empty_content_rate_pct,
CAST(100.0 * s.missing_reason_count / NULLIF(s.session_count, 0) AS decimal(6,2)) AS missing_reason_rate_pct,
CAST(100.0 * s.no_match_reason_count / NULLIF(s.session_count, 0) AS decimal(6,2)) AS no_match_reason_rate_pct,
CASE
WHEN (
100.0
- (100.0 * s.missing_reason_count / NULLIF(s.session_count, 0)) * 0.4
- (100.0 * s.no_match_reason_count / NULLIF(s.session_count, 0)) * 0.4
- (100.0 * s.empty_content_count / NULLIF(s.session_count, 0)) * 0.2
) < 0 THEN CAST(0 AS decimal(6,2))
ELSE CAST(
100.0
- (100.0 * s.missing_reason_count / NULLIF(s.session_count, 0)) * 0.4
- (100.0 * s.no_match_reason_count / NULLIF(s.session_count, 0)) * 0.4
- (100.0 * s.empty_content_count / NULLIF(s.session_count, 0)) * 0.2
AS decimal(6,2)
)
END AS experience_score
FROM shop_agg AS s
ORDER BY experience_score DESC, s.session_count DESC;
-- 4) 话术覆盖分析
SELECT
COUNT(*) AS session_count,
SUM(has_greeting) AS greeting_hit_count,
CAST(100.0 * SUM(has_greeting) / NULLIF(COUNT(*), 0) AS decimal(6,2)) AS greeting_hit_rate_pct,
SUM(has_empathy) AS empathy_hit_count,
CAST(100.0 * SUM(has_empathy) / NULLIF(COUNT(*), 0) AS decimal(6,2)) AS empathy_hit_rate_pct,
SUM(has_commitment) AS commitment_hit_count,
CAST(100.0 * SUM(has_commitment) / NULLIF(COUNT(*), 0) AS decimal(6,2)) AS commitment_hit_rate_pct,
SUM(has_closing) AS closing_hit_count,
CAST(100.0 * SUM(has_closing) / NULLIF(COUNT(*), 0) AS decimal(6,2)) AS closing_hit_rate_pct,
SUM(has_risk_phrase) AS risk_phrase_hit_count,
CAST(100.0 * SUM(has_risk_phrase) / NULLIF(COUNT(*), 0) AS decimal(6,2)) AS risk_phrase_hit_rate_pct
FROM #chat_norm;
-- 5) 风险话术命中 TOP
WITH phrase_hits AS (
SELECT
n.id,
n.order_no,
n.shop_name,
v.risk_phrase
FROM #chat_norm AS n
CROSS APPLY (VALUES
(CASE WHEN n.content_text LIKE N'%不知道%' THEN N'不知道' END),
(CASE WHEN n.content_text LIKE N'%没办法%' THEN N'没办法' END),
(CASE WHEN n.content_text LIKE N'%无法处理%' THEN N'无法处理' END),
(CASE WHEN n.content_text LIKE N'%不清楚%' THEN N'不清楚' END),
(CASE WHEN n.content_text LIKE N'%太慢%' THEN N'太慢' END),
(CASE WHEN n.content_text LIKE N'%等不了%' THEN N'等不了' END)
) AS v(risk_phrase)
WHERE v.risk_phrase IS NOT NULL
)
SELECT TOP (@limit)
risk_phrase,
COUNT(*) AS hit_count,
COUNT(DISTINCT id) AS session_count,
COUNT(DISTINCT order_no) AS order_count
FROM phrase_hits
GROUP BY risk_phrase
ORDER BY session_count DESC, hit_count DESC;
-- 6) 每日体验趋势
SELECT
CONVERT(date, createdAt) AS stat_date,
COUNT(*) AS session_count,
CAST(100.0 * SUM(is_no_match_reason) / NULLIF(COUNT(*), 0) AS decimal(6,2)) AS no_match_reason_rate_pct,
CAST(100.0 * SUM(is_missing_reason) / NULLIF(COUNT(*), 0) AS decimal(6,2)) AS missing_reason_rate_pct,
CAST(100.0 * SUM(is_empty_content) / NULLIF(COUNT(*), 0) AS decimal(6,2)) AS empty_content_rate_pct,
CAST(100.0 * SUM(has_risk_phrase) / NULLIF(COUNT(*), 0) AS decimal(6,2)) AS risk_phrase_hit_rate_pct
FROM #chat_norm
GROUP BY CONVERT(date, createdAt)
ORDER BY stat_date ASC;
-- 7) 对话案例(风险话术)+ 推荐回复
WITH case_pool AS (
SELECT
n.id,
n.order_no,
n.shop_name,
n.main_reason,
n.second_reason,
n.content_text,
CASE
WHEN n.content_text LIKE N'%不清楚%' THEN N'不清楚'
WHEN n.content_text LIKE N'%不知道%' THEN N'不知道'
WHEN n.content_text LIKE N'%没办法%' THEN N'没办法'
WHEN n.content_text LIKE N'%太慢%' THEN N'太慢'
WHEN n.content_text LIKE N'%等不了%' THEN N'等不了'
WHEN n.content_text LIKE N'%无法处理%' THEN N'无法处理'
ELSE NULL
END AS risk_tag
FROM #chat_norm AS n
)
SELECT TOP (@limit)
order_no,
shop_name,
main_reason,
second_reason,
risk_tag,
LEFT(REPLACE(REPLACE(ISNULL(content_text, N''), CHAR(13), N' '), CHAR(10), N' '), 220) AS dialogue_sample,
CASE
WHEN risk_tag IN (N'不清楚', N'不知道')
THEN N'建议回复:我先帮您核实一下该问题,预计5分钟内给您明确答复。'
WHEN risk_tag = N'没办法'
THEN N'建议回复:这个情况我来帮您申请处理方案,先给您两个可行选项并同步时效。'
WHEN risk_tag = N'无法处理'
THEN N'建议回复:我已升级给对应专员处理,预计今天内给您处理结果。'
WHEN risk_tag IN (N'太慢', N'等不了')
THEN N'建议回复:抱歉让您久等,我现在优先为您加急处理,并在10分钟内回报进展。'
ELSE N'建议回复:收到您的问题,我先确认订单与处理路径,稍后给您明确方案。'
END AS suggested_reply
FROM case_pool
WHERE risk_tag IS NOT NULL
ORDER BY id DESC;
-- 8) 客服能力评分(参考天猫服务保障维度,按可用字段做代理映射)
;WITH score_base AS (
SELECT
shop_name,
COUNT(*) AS session_count,
CAST(SUM(has_commitment) AS float) / NULLIF(COUNT(*), 0) AS commitment_rate,
CAST(SUM(has_empathy) AS float) / NULLIF(COUNT(*), 0) AS empathy_rate,
CAST(SUM(has_closing) AS float) / NULLIF(COUNT(*), 0) AS closing_rate,
CAST(SUM(has_risk_phrase) AS float) / NULLIF(COUNT(*), 0) AS risk_phrase_rate,
CAST(SUM(CASE WHEN second_reason LIKE N'%催退款%' OR second_reason LIKE N'%催取件%' THEN 1 ELSE 0 END) AS float) / NULLIF(COUNT(*), 0) AS refund_urge_rate,
CAST(SUM(is_no_match_reason) AS float) / NULLIF(COUNT(*), 0) AS no_match_rate,
CAST(SUM(is_missing_reason) AS float) / NULLIF(COUNT(*), 0) AS missing_reason_rate
FROM #chat_norm
GROUP BY shop_name
),
score_calc AS (
SELECT
sb.*,
100.0 * (0.70 * sb.commitment_rate + 0.30 * (1.0 - sb.risk_phrase_rate)) AS resp_ability_score,
100.0 * (0.45 * sb.empathy_rate + 0.35 * sb.closing_rate + 0.20 * (1.0 - sb.risk_phrase_rate)) AS satisfaction_score,
100.0 * (1.0 - sb.refund_urge_rate) AS refund_efficiency_score,
100.0 * (1.0 - (0.80 * sb.no_match_rate + 0.20 * sb.missing_reason_rate)) AS help_risk_control_score
FROM score_base AS sb
)
SELECT TOP (@limit)
shop_name,
session_count,
CAST(resp_ability_score AS decimal(6,2)) AS resp_ability_score,
CAST(satisfaction_score AS decimal(6,2)) AS satisfaction_score,
CAST(refund_efficiency_score AS decimal(6,2)) AS refund_efficiency_score,
CAST(help_risk_control_score AS decimal(6,2)) AS help_risk_control_score,
CAST(
0.30 * resp_ability_score
+ 0.30 * satisfaction_score
+ 0.20 * refund_efficiency_score
+ 0.20 * help_risk_control_score
AS decimal(6,2)
) AS service_capability_score,
CASE
WHEN (
0.30 * resp_ability_score
+ 0.30 * satisfaction_score
+ 0.20 * refund_efficiency_score
+ 0.20 * help_risk_control_score
) >= 90 THEN N'S'
WHEN (
0.30 * resp_ability_score
+ 0.30 * satisfaction_score
+ 0.20 * refund_efficiency_score
+ 0.20 * help_risk_control_score
) >= 80 THEN N'A'
WHEN (
0.30 * resp_ability_score
+ 0.30 * satisfaction_score
+ 0.20 * refund_efficiency_score
+ 0.20 * help_risk_control_score
) >= 70 THEN N'B'
WHEN (
0.30 * resp_ability_score
+ 0.30 * satisfaction_score
+ 0.20 * refund_efficiency_score
+ 0.20 * help_risk_control_score
) >= 60 THEN N'C'
ELSE N'D'
END AS service_grade,
N'映射说明: 响应能力≈旺旺响应能力; 满意度≈旺旺满意度; 退款效率≈退款处理体验; 求助风险控制≈平台求助风险(代理口径)' AS score_mapping_note
FROM score_calc
ORDER BY service_capability_score DESC, session_count DESC;