-- ============================================================================= -- 病区出院随访统计 · 可传参视图 -- ============================================================================= -- 为什么是「参数表 + 视图」而不是 @变量: -- MySQL 视图定义里禁止引用 @用户变量 / 系统变量 / 存储过程参数, -- 否则创建时报 ERROR 1351 (HY000): View's SELECT contains a variable or parameter。 -- 官方原文:「The SELECT statement cannot refer to system variables or -- user-defined variables.」 -- https://dev.mysql.com/doc/refman/8.0/en/create-view.html -- -- 所以用单行参数表 report_param 承载参数: -- 1) REPLACE INTO report_param ... 改第 1 行(起止时间 / 口径 / 及时天数) -- 2) SELECT * FROM v_ward_followup_stats; -- -- 并发提醒:参数表是共享状态,两个会话同时改会互相串。多用户并发请改用 -- 同目录的 p_ward_followup_stats.sql(存储过程,真正按调用传参)。 -- ============================================================================= -- --------------------------------------------------------------------------- -- 1. 参数表(永远只有 id = 1 这一行) -- --------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS report_param ( id TINYINT NOT NULL COMMENT '固定为 1', start_time DATETIME NOT NULL COMMENT '出院时间起(含),传 yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss 均可', end_time DATETIME NOT NULL COMMENT '出院时间止(含当天 23:59:59),传 yyyy-MM-dd 即可', count_style VARCHAR(2) NOT NULL DEFAULT '1' COMMENT '随访方式命中口径:1=应随访 3=仅成功 其它=全部', timely_days INT NOT NULL DEFAULT 7 COMMENT '及时率的天数基数(补偿天数另加)', PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '报表参数(单行,id 恒为 1)'; -- --------------------------------------------------------------------------- -- 2. 视图 -- --------------------------------------------------------------------------- CREATE OR REPLACE VIEW v_ward_followup_stats AS 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(p.count_style = '1', IF(ss.sendstate IS NOT NULL AND ss.sendstate <> 4, 1, 0), IF(p.count_style = '3', IF(ss.sendstate = 6, 1, 0), IF(ss.sendstate IS NOT NULL, 1, 0))) AS ch_hit FROM service_subtask ss JOIN report_param p ON p.id = 1 WHERE ss.del_flag = 0 AND ss.service_type = '2' AND ss.visit_count = 1 -- 只要首次随访 AND ss.leavehospitaldistrictcode IS NOT NULL -- >>> 可传参的时间范围:左闭右开,end_time 传日期即可含当天全天 <<< AND ss.endtime >= DATE(p.start_time) AND ss.endtime < DATE_ADD(DATE(p.end_time), 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) <= p.timely_days + IFNULL(cp.compensate_days, 0), 1, 0)) AS timely_cnt FROM service_subtask ss JOIN report_param p ON p.id = 1 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(p.start_time) AND ss.endtime < DATE_ADD(DATE(p.end_time), INTERVAL 1 DAY) AND ss.orgid = '20001001' GROUP BY ss.leavehospitaldistrictcode ) rt ON rt.ward_code = t.ward_code ORDER BY t.dischargeCount DESC; -- --------------------------------------------------------------------------- -- 3. 用法 -- --------------------------------------------------------------------------- -- 改参数(只改这一行,两个日期就是原来的 endtime 起止) REPLACE INTO report_param (id, start_time, end_time, count_style, timely_days) VALUES (1, '2026-10-04', '2026-11-03', '1', 7); -- 查数 SELECT * FROM v_ward_followup_stats -- WHERE leavehospitaldistrictname LIKE '%骨科%' -- 视图外照样能再加条件 ;