陈昶聿
55 分钟以前 9799e992bc987425f21ad0b799e3d25d822329e3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
-- =============================================================================
-- 病区出院随访统计 · 存储过程(推荐,真正按调用传参)
-- =============================================================================
-- MySQL 视图不能带参数(视图定义里连 @变量 都不许用),要「传参」就用存储过程。
--
-- 调用:
--   CALL p_ward_followup_stats('2026-10-04', '2026-11-03', '1', 7);
--   CALL p_ward_followup_stats('2026-10-04', '2026-11-03', NULL, NULL);  -- 口径/及时天数用默认值
--   (MySQL 存储过程没有「可选参数」,四个参数都必须写,不需要的传 NULL)
--
-- 参数:
--   p_start_time  出院时间起(含),'yyyy-MM-dd' 或 'yyyy-MM-dd HH:mm:ss'
--   p_end_time    出院时间止(含当天 23:59:59),传 'yyyy-MM-dd' 即可
--   p_count_style 随访方式命中口径:1=应随访(默认) 3=仅成功 其它=全部
--   p_timely_days 及时天数基数,默认 7(补偿天数另加)
-- =============================================================================
 
DROP PROCEDURE IF EXISTS p_ward_followup_stats;
 
DELIMITER $$
CREATE PROCEDURE p_ward_followup_stats(
  IN p_start_time  DATETIME,
  IN p_end_time    DATETIME,
  IN p_count_style VARCHAR(2),
  IN p_timely_days INT
)
BEGIN
  DECLARE v_start       DATETIME;
  DECLARE v_end         DATETIME;
  DECLARE v_style       VARCHAR(2);
  DECLARE v_timely_days INT;
 
  -- 起止时间必传;止时间按「包含当天」处理
  IF p_start_time IS NULL OR p_end_time IS NULL THEN
    SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'p_start_time / p_end_time 不能为空';
  END IF;
 
  SET v_start       = DATE(p_start_time);
  SET v_end         = DATE_ADD(DATE(p_end_time), INTERVAL 1 DAY);
  SET v_style       = IFNULL(p_count_style, '1');
  SET v_timely_days = IFNULL(p_timely_days, 7);
 
  SELECT
    t.leavehospitaldistrictname,                                -- 出院病区
    t.deptname,                                                 -- 出院科室
    t.dischargeCount,                                           -- 出院人次(首次随访记录数)
    t.nonFollowUp,                                              -- 无需随访人次
    t.followUpNeeded,                                           -- 应随访人次
    t.needFollowUp,                                             -- 首次应随访(= 应随访人次)
    t.pendingFollowUp,                                          -- 首次待随访
    t.followUpSuccess,                                          -- 首次随访成功
    t.followUpFail,                                             -- 首次随访失败(含人工超时)
    t.manualSuccess,                                            -- 首次人工随访成功
    t.voiceSuccess,                                             -- 首次语音随访成功
    t.smsSuccess,                                               -- 首次短信随访成功
    t.weChatSuccess,                                            -- 首次微信随访成功
    -- 随访成功率 = 成功 / 应随访(只算成功,不含失败)
    IF(t.needFollowUp = 0, '0.00%',
       CONCAT(ROUND(100 * t.followUpSuccess / t.needFollowUp, 2), '%'))   AS followUpSuccessRate,
    -- 渠道合计成功率 = 四种方式成功之和 / 四种方式命中之和
    IF(t.manual + t.voice + t.sms + t.weChat = 0, '0.00%',
       CONCAT(ROUND(100 * (t.manualSuccess + t.voiceSuccess + t.smsSuccess + t.weChatSuccess)
                    / (t.manual + t.voice + t.sms + t.weChat), 2), '%'))   AS channelSuccessRate,
    -- 及时率:及时 = finishtime 不为空 且 DATEDIFF(finishtime, endtime) <= 及时天数 + 补偿天数
    IF(IFNULL(rt.timely_base, 0) = 0, '0.00%',
       CONCAT(ROUND(100 * rt.timely_cnt / rt.timely_base, 2), '%'))       AS timelyRate
  FROM (
    SELECT
      r.ward_code                                    AS ward_code,
      MAX(r.ward_name)                               AS leavehospitaldistrictname,
      MAX(r.dept_name)                               AS deptname,
      COUNT(*)                                       AS dischargeCount,
      SUM(r.f_no)                                    AS nonFollowUp,
      SUM(r.f_need)                                  AS followUpNeeded,
      SUM(r.f_need)                                  AS needFollowUp,
      SUM(IF(r.sendstate = 2, 1, 0))                 AS pendingFollowUp,
      SUM(r.f_succ)                                  AS followUpSuccess,
      SUM(r.f_fail)                                  AS followUpFail,
      SUM(IF(r.ch_hit = 1 AND r.cp = '1', 1, 0))     AS manual,
      SUM(IF(r.ch_hit = 1 AND r.cp = '3', 1, 0))     AS voice,
      SUM(IF(r.ch_hit = 1 AND r.cp = '4', 1, 0))     AS sms,
      SUM(IF(r.ch_hit = 1 AND r.cp = '5', 1, 0))     AS weChat,
      SUM(IF(r.sendstate = 6 AND r.cp = '1', 1, 0))  AS manualSuccess,
      SUM(IF(r.sendstate = 6 AND r.cp = '3', 1, 0))  AS voiceSuccess,
      SUM(IF(r.sendstate = 6 AND r.cp = '4', 1, 0))  AS smsSuccess,
      SUM(IF(r.sendstate = 6 AND r.cp = '5', 1, 0))  AS weChatSuccess
    FROM (
      SELECT
        ss.leavehospitaldistrictcode                              AS ward_code,
        ss.leavehospitaldistrictname                              AS ward_name,
        ss.leaveldeptname                                         AS dept_name,
        ss.sendstate                                              AS sendstate,
        ss.current_preachform                                     AS cp,
        IF(ss.sendstate = 4, 1, 0)                                AS f_no,
        IF(ss.sendstate IS NOT NULL AND ss.sendstate <> 4, 1, 0)  AS f_need,
        IF(ss.sendstate = 6, 1, 0)                                AS f_succ,
        IF(ss.sendstate IN (5, 7), 1, 0)                          AS f_fail,
        -- 随访方式命中条件:方式 1 排除「无需随访」;方式 3 只算已完成;其它方式不设限
        IF(v_style = '1',
           IF(ss.sendstate IS NOT NULL AND ss.sendstate <> 4, 1, 0),
           IF(v_style = '3',
              IF(ss.sendstate = 6, 1, 0),
              IF(ss.sendstate IS NOT NULL, 1, 0)))                AS ch_hit
      FROM service_subtask ss
      WHERE ss.del_flag = 0
        AND ss.service_type = '2'
        AND ss.visit_count = 1                                  -- 只要首次随访
        AND ss.leavehospitaldistrictcode IS NOT NULL
        AND ss.endtime >= DATE(v_start)
        AND ss.endtime <  DATE_ADD(DATE(v_end), INTERVAL 1 DAY)
        AND ss.orgid = '20001001'
    ) r
    GROUP BY r.ward_code
  ) t
  LEFT JOIN (
    -- 及时率:按病区预聚合一次再关联,避免一对多放大分母
    SELECT
      ss.leavehospitaldistrictcode AS ward_code,
      COUNT(*) AS timely_base,
      SUM(IF(ss.finishtime IS NOT NULL
             AND DATEDIFF(ss.finishtime, ss.endtime) <= v_timely_days + IFNULL(cp.compensate_days, 0),
             1, 0)) AS timely_cnt
    FROM service_subtask ss
    LEFT JOIN (
      -- 补偿时间:varchar 且可能是空串,先按子任务聚合成一行
      SELECT subid,
             SUM(CAST(COALESCE(NULLIF(TRIM(compensate_time), ''), '0') AS SIGNED)) AS compensate_days
      FROM service_subtask_preachform
      WHERE del_flag = 0
      GROUP BY subid
    ) cp ON cp.subid = ss.id
    WHERE ss.del_flag = 0
      AND ss.service_type = '2'
      AND ss.visit_count = 1
      AND ss.leavehospitaldistrictcode IS NOT NULL
      AND ss.visit_time IS NOT NULL
      AND ss.sendstate <> 4
      AND DATE(ss.visit_time) < DATE(NOW())     -- 接口未传 startTime/endTime 时的固定条件
      AND ss.endtime >= DATE(v_start)
      AND ss.endtime <  DATE_ADD(DATE(v_end), INTERVAL 1 DAY)
      AND ss.orgid = '20001001'
    GROUP BY ss.leavehospitaldistrictcode
  ) rt ON rt.ward_code = t.ward_code
  ORDER BY t.dischargeCount DESC;
END$$
DELIMITER ;