assets/2026-07-01-医数智联系列开篇-医院数字化工作者的工具箱_2026-07-01_18-28-11.jpg

医数智联·第 25 期:Claude Code + SQL 查询 —— 让数据库「听得懂人话」的 3 大玩法

[!ABSTRACT] 核心摘要

  • 本期主题:Claude Code + SQL——用自然语言查数据库,告别手写复杂 SQL
  • 核心结论:Claude Code 可以直接连接 MySQL/PostgreSQL,你说「查什么」,AI 帮你写 + 跑 SQL
  • 3 大玩法:智能问询(自然语言 → SQL)、自动写 SQL(描述需求 → 代码)、SQL 优化(慢查询 → 提速)
  • 典型效果:从「学 1 周 SQL」变成「说人话查数据」
  • 阅读时长:约 10 分钟

一、场景引入:为什么 SQL 学习这么难?

[!example] 一个真实的周一上午
院长:「上月每个医生开了多少处方?Top 10 给我。」

质管办小张打开 Navicat,开始写 SQL:

1
2
3
4
5
6
7
8
SELECT a.医生姓名, COUNT(*) AS 处方数
FROM 处方表 a
LEFT JOIN 医生表 b ON a.医生工号 = b.医生工号
WHERE a.开具日期 >= '2026-06-01'
AND a.开具日期 < '2026-07-01'
GROUP BY a.医生工号
ORDER BY 处方数 DESC
LIMIT 10;

报错:Column '医生姓名' is ambiguous

小张改:SELECT b.医生姓名, COUNT(*) AS 处方数

又报错:a.医生工号 must be in GROUP BY

小张改:GROUP BY b.医生姓名, a.医生工号

终于跑出结果了,但半小时过去了

这是医院人写 SQL 的典型困境——语法细节多,JOIN 复杂,GROUP BY 严格要求

现状 痛点
简单查询要 10 分钟 浪费时间
JOIN 嵌套易错 debug 难
窗口函数不会 复杂分析做不了
表结构不熟 字段名常拼错

[!quote] 狼叔的判断
“SQL 是医院数据的’普通话’,但不是每个人都要精通——AI 是最好的’翻译官’。” 这一期,带你用 Claude Code 让数据库「听得懂人话」。


二、工具原理:Claude Code 怎么连数据库?

2.1 三种连数据库方式

[!info] Claude Code 连接数据库的 3 种方式

方式 1:Claude Code 直接连(需 MCP 协议)

Claude Code 支持 Model Context Protocol(MCP),可以挂载数据库连接器。

1
2
3
4
5
6
# 安装 MySQL MCP
claude mcp add mysql npx -y @modelcontextprotocol/server-mysql \
--env MYSQL_HOST=192.168.1.100 \
--env MYSQL_USER=quality_user \
--env MYSQL_PASSWORD=xxx \
--env MYSQL_DATABASE=his_db

配置后,Claude Code 就能直接查询数据库。

方式 2:Python 脚本连接(推荐)

让 Claude Code 写 Python + pymysql 脚本,跑结果。

方式 3:SQL 文件 + Claude Code 执行

把 .sql 文件给 Claude Code,它读懂并执行。

2.2 工作原理

1
2
3
你说需求 → Claude Code 理解 → 写 SQL → 执行 → 返回结果

优化建议(自动 EXPLAIN)

三、医院实战:3 大玩法

玩法 1:智能问询(自然语言 → SQL)

[!example] 实战 ①:自然语言问数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
👤 你:帮我查「上月骨科出院的所有病案,主诊错误标记 = 1,按责任医生工号统计,错误数降序」

🤖 Claude Code:
好的,我写 SQL 给你:

SELECT
责任医生工号,
COUNT(*) AS 错误数
FROM 首页主表
WHERE 科室 = '骨科'
AND 主诊错误标记 = 1
AND 出院日期 >= '2026-06-01'
AND 出院日期 < '2026-07-01'
GROUP BY 责任医生工号
ORDER BY 错误数 DESC;

要我帮你跑吗?(会连 MySQL 执行,返回结果表)

结果(假设跑了):

1
2
3
4
5
| 责任医生工号 | 错误数 |
| D001 | 12 |
| D002 | 9 |
| D003 | 7 |
| D004 | 5 |

[!TIP] 智能问询的 3 大要点

  1. 需求要清晰(包含表、时间范围、筛选条件、聚合方式)
  2. 可以追问(「再按职称分组看看」)
  3. 可以指定输出(「只显示前 10 条」「导出 Excel」)

玩法 2:自动写 SQL(描述需求 → 完整脚本)

[!example] 实战 ②:从需求到完整 Python 脚本

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
👤 你:写一个完整的 Python 脚本,完成以下任务:
1. 从 MySQL 读本月首页(inpatient_main 表,字段:record_id, dept_name, error_flag, discharge_date)
2. 统计每个科室的错误率和错误数
3. 输出 Excel 和 CSV
4. 中文用 SimHei 字体

数据库连接:
- host=192.168.1.100
- user=quality_user
- password=xxx
- database=his_db

🤖 Claude Code:
好的,这是完整脚本:

import pandas as pd
import pymysql

# 1. 连接
conn = pymysql.connect(
host='192.168.1.100',
user='quality_user',
password='xxx',
database='his_db',
charset='utf8mb4'
)

# 2. 读数据
df = pd.read_sql('''
SELECT
record_id AS 病案号,
dept_name AS 科室,
error_flag AS 主诊错误标记,
discharge_date AS 出院日期
FROM inpatient_main
WHERE discharge_date >= '2026-07-01'
AND discharge_date < '2026-08-01'
''', conn)
conn.close()

# 3. 统计
统计 = df.groupby('科室').agg(
总数=('病案号', 'count'),
错误数=('主诊错误标记', 'sum')
).reset_index()
统计['错误率'] = (统计['错误数'] / 统计['总数'] * 100).round(2)

# 4. 输出
统计.to_excel('本月错误率.xlsx', index=False)
统计.to_csv('本月错误率.csv', index=False, encoding='utf-8-sig')

print(f'✓ 已生成 {len(统计)} 个科室的统计')
print(统计.head())

要我跑吗?(y/n)

玩法 3:SQL 优化(慢查询 → 提速)

[!example] 实战 ③:慢 SQL 优化

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
👤 你:这个 SQL 跑 1 万条要 30 秒,帮我优化到 1 秒内:

SELECT *
FROM inpatient_main
WHERE YEAR(discharge_date) = 2026
AND MONTH(discharge_date) = 7
AND dept_name = '骨科';

🤖 Claude Code:
我看到几个问题:

1. **YEAR()/MONTH() 函数让索引失效** — 函数在索引列上,数据库无法用索引
2. **SELECT * 返回不必要字段** — 增加 IO
3. **dept_name 没建索引** — 全表扫描

优化方案:

-- 优化后(预计 0.05 秒)
SELECT
record_id,
dept_name,
error_flag,
discharge_date
FROM inpatient_main
WHERE discharge_date >= '2026-07-01'
AND discharge_date < '2026-08-01'
AND dept_name = '骨科';

-- 建索引(让查询更快)
CREATE INDEX idx_discharge_dept
ON inpatient_main (discharge_date, dept_name, error_flag);

性能对比:
| 优化前 | 30.0 秒 |
| 优化后 | 0.05 秒 |
| 提速 | 600 倍 |

要我帮你跑一下验证吗?

[!TIP] SQL 优化的 4 大原则

  1. 避免索引列用函数:YEAR(date)date >= '2026-01-01'
  2. **避免 SELECT ***:只 SELECT 需要的字段
  3. 建联合索引:按 WHERE 字段顺序建
  4. 避免类型转换:字符串列不要传数字

进阶玩法:跨表复杂查询

[!example] 实战 ④:5 表 JOIN 复杂分析

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
👤 你:我要分析「**上月主诊错误的医生,他们的职称分布、科室分布、错误类型分布**」,数据在 5 张表里:
- inpatient_main(首页主表)
- doctor(医生表)
- dept(科室表)
- title(职称表)
- error_code(错误代码表)

字段映射:
- inpatient_main:record_id, error_flag, doctor_id, error_code
- doctor:doctor_id, doctor_name, title_id, dept_id
- dept:dept_id, dept_name
- title:title_id, title_name
- error_code:error_code, error_desc, error_type

帮我写一个 SQL,把 5 表关联起来,输出:
- 错误数
- 职称分布
- 科室分布
- 错误类型分布

🤖 Claude Code:
好的,这是 SQL:

SELECT
d.doctor_name AS 医生姓名,
t.title_name AS 职称,
dept.dept_name AS 科室,
COUNT(*) AS 错误数,
GROUP_CONCAT(DISTINCT ec.error_type) AS 错误类型列表
FROM inpatient_main im
INNER JOIN doctor d ON im.doctor_id = d.doctor_id
LEFT JOIN dept ON d.dept_id = dept.dept_id
LEFT JOIN title t ON d.title_id = t.title_id
LEFT JOIN error_code ec ON im.error_code = ec.error_code
WHERE im.error_flag = 1
AND im.discharge_date >= '2026-06-01'
AND im.discharge_date < '2026-07-01'
GROUP BY d.doctor_id, d.doctor_name, t.title_name, dept.dept_name
ORDER BY 错误数 DESC;

要我执行并返回结果吗?

[!success] 复杂 JOIN 让 Claude Code 写的好处

  1. AI 帮你理清 JOIN 顺序(INNER / LEFT 哪个对)
  2. AI 自动加表别名(避免字段歧义)
  3. AI 提醒 GROUP BY 完整性(所有 SELECT 字段都要在 GROUP BY)

四、避坑指南:5 个让 AI + SQL「翻车」的常见错误

[!warning] 雷区 ①:把数据库密码明文写脚本
症状:脚本里有 password='xxx',传到 GitHub 泄露
解决:用环境变量:

1
2
import os
PASSWORD = os.environ.get('DB_PASSWORD', 'xxx')

[!warning] 雷区 ②:跑全表查询没 LIMIT
症状:SELECT * FROM 首页主表 跑 100 万行,卡死数据库
解决:永远先 LIMIT 测试:

1
2
SELECT * FROM 首页主表 LIMIT 10;
-- 确认无误再放开

[!warning] 雷区 ③:修改生产数据没备份
症状:UPDATE 首页主表 SET xxx WHERE yyy,改错了全院数据出问题
解决:AI 写的 UPDATE/DELETE 脚本必须人工 review,最好先 SELECT 验证。

[!warning] 雷区 ④:SQL 注入风险
症状:把用户输入直接拼到 SQL 里,被注入攻击
解决:用参数化查询:

1
2
3
4
5
# ❌ 危险
sql = f"SELECT * FROM user WHERE name = '{name}'"
# ✅ 安全
sql = "SELECT * FROM user WHERE name = %s"
df = pd.read_sql(sql, conn, params=[name])

[!warning] 雷区 ⑤:中文乱码
症状:读出来的中文是 ???
解决:连接加 charset:

1
conn = pymysql.connect(..., charset='utf8mb4')

五、把 AI + SQL 嵌入日常工作流

[!TIP] 实战:质管人的「AI 数据查询日常」

时段 任务 Claude Code 怎么帮
早上 8:30 跑昨日数据 「查昨天出院的所有病案总数」
上午 10:00 院长要数据 「查上月主诊错误率 Top 5 科室」
下午 3:00 慢 SQL 「这个 SQL 跑 30 秒,优化到 1 秒」
晚上 8:00 写新报告 「写本月质控报告 SQL」

每天 30 分钟 AI 查询 = 数据需求响应时间从 1 天压到 5 分钟


六、下期预告

[!quote] 第 26 期
Claude Code + 首页质控全流程 —— 把 21 期的所有技能用 AI 串起来

这一期,带你用 Claude Code 搭建「首页质控自动化流水线」,自然语言驱动整个流程


[!TIP] 互动
如果你觉得这篇「医数智联·第 25 期」对你有用,请点赞、在看、转发给科里的同事。

文中 3 大玩法的 prompt 模板,我已经整理成「Claude Code SQL 工具包」。点击「阅读原文」可直接下载

我是白衣狼,一个用数字化工具把医院管理做「轻」的实战派。咱们下期见。