275 lines
12 KiB
Transact-SQL
275 lines
12 KiB
Transact-SQL
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;
|