ICU数据库表结构设计与SQL取数全解析:从字段命名到数据质量校验 简介文档系统整理了中国四川省自贡市第四人民医院ICU数据库的核心数据表结构与字段含义面向从事重症医学临床研究、医疗数据分析及相关课题的研究人员。内容涵盖dtBaseline基线信息、dtDrugs药物、dtICD诊断、dtLab实验室、dtTransfer转诊、dtNursingChart护理记录等核心表并对每张表的字段名称、数据类型、隐私脱敏规则及常见数据质量问题做了说明尤其针对感染或脓毒症患者的高粒度数据特点展开。资源为单独1个docx文件压缩包大小约31KB便于查阅与打印。该数据库纳入了2790名ICU成年患者数据文档中同时指出了急诊类型混乱、年龄脱敏、护理表缺失值多等使用注意点可帮助研究者快速理解数据字典、规避预处理陷阱提升后续建模与统计分析效率。已有548人学习下载适合需要利用该ICU数据库开展疾病预后、疗效评估或预测模型研究的用户。1. 一份ICU数据表介绍文档为什么值得从头读到字段级做ICU数据的人经常把精力花在SQL语法和大屏可视化上等到真正跑数才发现连“一次住院”在表里都未必是一行记录。四川省自贡市第四人民医院ICU数据库的数据表文档名字听着只是表结构说明实际上是整套取数逻辑的字典它规定了一次ICU住院如何拆成入科出科、分钟级生命体征、护理评分、检验结果、操作事件等几十张表每张表有自己的粒度、主键和时间口径。读不懂这个分层就会把半小时一次的护理记录当成连续波形把重复入科当成两次住院。这份文档要解决的是“表怎么组织、字段怎么理解、数据怎么对得上”三件事适合要做ICU预后分析、临床科研取数、指标看板却对着原始表无从下手的工程师。它的价值不在文档本身而在让你拿到任何一张表都能说清三个问题这张表一行代表什么、时间精度是多少、和别的表靠什么关联。2. ICU库的表结构从一张住院主表延伸到几十张子表的径向设计ICU数据库不是一张塞满所有字段的大宽表而是围绕“一次ICU住院”展开的多表径向结构。理解这一点比记住任何一张表的字段列表都重要。2.1 为什么ICU数据必须拆表而不是拉平ICU一天产生的数据量级远超普通病区。心电、血压、血氧这类监护仪原始波形如果直接记录单患者一天就有海量行护理人员每隔半小时到一小时记录的生命体征又是一套频次检验科送来的血气、生化又是独立的报告结构呼吸机、CRRT这些设备还有各自的参数快照。把这些不同频次、不同来源的数据强行拉平到一张表结果是大部分单元格为空表体积膨胀到无法查询。这套ICU库的设计沿用了医疗数据常见的“事件径向扩展”模式。主表记录每一次ICU入住事件子表按不同的业务域和时间粒度分别存储。优点是每种数据的写入频次和保留策略可以独立设置缺点是要求取数人员对表间关系有清晰认识。拿到表文档先不要急着看字段先看表清单确认哪些是主表、哪些是明细表、哪些是字典表。2.2 入科表与出科表一次ICU住院的边界定义入科表是整套库的锚点每一行代表一次入住ICU的事件记录表文档中通常包含科室信息、入科时间、出科时间、住院号、患者基本信息索引等。实际库中出科信息可能独立成表也可能并入入科表取决于文档作者的建模风格。以这套库的常规设计入科记录和出科记录放在同一张主表的情况更常见字段用入科时间、出科时间两个时间戳来限定边界。需要注意一个患者在院期间可能多次进出ICU所以“住院号唯一”不等于“入科表行数唯一”。做时长统计时必须以入科记录的唯一标识分组而不是按住院号分组否则会把两次ICU住院的时间叠加成一次。下文还会细讲这个坑但先从主表层面记住结论入科表的主键通常是入科记录号不是住院号。2.3 分钟级与小时级生命体征表频次不同用途不同生命体征是ICU数据里最常用的子表但往往被拆成多张。监护仪连续采集的数据如果逐秒记录单日单床的数据量就非常夸张所以大多会做降采样处理。这套文档里常见的设计是分钟级生命体征表和小时级趋势表并存分钟级表保存最近数天的细粒度记录小时级表按分钟数据聚合出均值、最小值、最大值用于长期趋势回顾和科研特征提取。这里直接决定了后面SQL怎么写。分析“入ICU后24小时平均动脉压的变化曲线”该用分钟级表评估“入ICU第3天是否出现低血压事件”该查小时级表里的小时最低值要做血压变异性的计算则必须回到分钟级原始记录。分不清粒度重采样会引入偏差代码写得再漂亮都是给错误数据打工。2.4 事件、操作与评分表时间轴上打点的非数值记录除了规律采集的生命体征ICU还有大量不规则产生的记录。插管时间、拔管时间、CRRT上机下机、镇静药物推注这类操作各自成表GCS评分、SOFA评分、APACHE II评分按评估频次散落在评分表中。它们共同的特点是行数少、时间点不规则、每行代表一次“发生了某件事”或“做了一次评估”。这些表是临床分析和流程优化的重要数据来源。但要留意的是操作事件表里的时间字段记录的是医嘱执行时间还是操作结束时间文档里未必写到字段注释里往往要从文档对流程的描述去推断。这个推断会直接影响到“插管到拔管时长”这类指标的准确性。评估类表的另一个特征是同一个评分可能有多个版本或不同评估人设计不严谨的库会在同一时段出现两行GCS不一致的记录取数时要有去重规则。2.5 表间关联方式主键、外键与时间对齐这套库的表间关联基本遵循两种模式一是通过患者住院主表的外键关联二是通过时间窗口关联。前半者是稳定的主表给出的入科记录号可以在所有子表中找到对应标记时间关联则要小心ICU里的“同时刻”可能因为设备时钟同步问题差出几十秒。表文档里给出的关联规范多数只有主键和外键说明不会专门讨论时钟偏移但实际数据里经常发生。严谨的做法是能通过外键关联的查询绝不依赖时间模糊匹配必须用时间关联时给两个时间字段都保留原始精度并记录偏移量后续数据质控环节再统一校准。3. 字段设计的实用读法命名规范、时间格式与值域约定技术人员拿到数据表介绍文档往往会跳过字段细节直接写查询。但在ICU这一场景字段读错了比SQL写错了更致命。因为ICU的表字段有几个约定俗成的习惯文档里写了但你不一定意识到它对应什么落地含义。3.1 字段命名的分组规则哪组变量名称是合理的字段命名看起来只是风格问题实际关系到你能不能快速推断出一个字段的单位和含义。这套ICU库文档中的字段命名有相对清晰的分组逻辑患者标识类以PT_ID、ADM_ID、ICU_REC_ID为主标识前缀时间类字段统一以_TIME后缀结束生命体征值用英文缩写加单位后缀如HR_BPM、MAP_MMHG、SPO2_PCT。这种命名不是偶然它构成了一个规矩看到 _ID结尾的都可以当关联键看到 _TIME结尾的都是时间戳看到带单位的字段名就直接可用而不用到处查字典。从数据工程角度判断一个字段名是否合理就看三件事能不能望文生义、单位是否在名字里体现、相同含义字段在不同表中是否同名。文档中三类常见的不合理命名也能一眼识别中文拼音混用、相同指标在不同表中使用不同名词、值字段不写单位。实际工作中遇到这种表最好建立一张字段映射表否则三个月后再看数据会完全失去上下文。3.2 时间字段的三种写法统一性与时间精度陷阱这套文档中时间字段出现过几种格式精确到秒的日期时间字符串、日期加小时、Unix时间戳。三种写法并存的原因大概率是不同源系统的导出格式不同对接时做了取舍而不是统一转换。在取数时这会造成一个非常隐蔽的问题字符比较和日期函数混用后排序和范围查询的结果不一致。落地建议是读文档时先查所有 _TIME 结尾字段的格式声明只要存在字符串格式的时间字段查询就统一先做CAST再比较。同时确认每个时间字段的语义是“记录产生时间”还是“业务发生时间”两者在数据回传延迟的场景下可能相差数小时。ICU设备断网后补传数据的情况不少只依赖记录产生时间做时间窗过滤会漏掉当时真实发生的事件。3.3 值域与单位写在字段名里还是写在值里生命体征表最容易出现单位混乱的地方是体温。常见记录方式包括摄氏度、华氏度、甚至不带单位直接写数值。在这套ICU文档里规范的字段名会直接命名为TEMP_C列即数值单位后缀但部分嵌套表或自由文本字段仍然会存在“43.2”这种裸数值。遇到无单位字段唯一的确认路径是看文档的取值说明或源系统录入规范不能靠猜测。同样值得注意的还有血压字段的存储方式。有的表把收缩压和舒张压分开两个字段存有的合并在一个字段写成“120/80”后者在一个字段里包含了两个值做数值计算前必须先拆分。如果文档中有“数据质量说明”章节通常会对这类字段进行专项描述ETL时的处理方式应对应写在字段映射文档里。3.4 缺失值标记别把一个标记当成三种含义ICU数据表的缺失值标记在不同表中并不一致。常见的有SQL中的NULL、空字符串、数值占位-1或999以及按临床惯例的“无法测得”。文档中如果标注字段允许为空说明缺失是预期的如果用数值占位标记缺失做统计时一定要先过滤否则平均值会被-1严重拉低。这里有一个容易被忽略的细节临床记录中生命体征为空值和生命体征不测量的处理方式不同。比如某段时间患者被转运去检查监护仪离线记录为空另一种情况是护士按时巡视但患者躁动无法测量记录为“未测”。两种场景都对应空值但业务含义截然不同。表文档不会区分这两者取数人员要在需求分析阶段确认你关心的是“没有数据”还是“当时没有测量”4. 从文档到实际查询按时间窗、按事件、按频次写SQL的三种典型做法字段读熟之后下一步是把文档描述转换成可跑的SQL。ICU数据查询绝大多数集中在三种模式按时间窗提取数据、按事件对齐多张表、按频次重采样。下面以这套库的典型表结构给出最小可用查询参数按常见临床分析场景设定。4.1 最小可用查询取一段时间内的生命体征最常见的需求是“提取某位患者入ICU后24小时内的分钟级血压和心率”。假设入科表名为icu_admission分钟级生命体征表名为vitals_min两个表结构如下icu_admissionicu_record_id, patient_id, adm_time, dis_time vitals_minicu_record_id, record_time, hr_bpm, sbp_mmhg, dbp_mmhg, map_mmhg, spo2_pctWITH target_adm AS ( SELECT icu_record_id, adm_time, dis_time FROM icu_admission WHERE patient_id P123456 AND adm_time 2025-01-01 00:00:00 AND adm_time 2025-02-01 00:00:00 ) SELECT v.record_time, v.hr_bpm, v.sbp_mmhg, v.dbp_mmhg, v.map_mmhg, v.spo2_pct FROM vitals_min v JOIN target_adm t ON v.icu_record_id t.icu_record_id WHERE v.record_time t.adm_time AND v.record_time t.adm_time INTERVAL 24 HOUR ORDER BY v.record_time;这段SQL的关键是把入科记录作为过滤锚点而不是直接按patient_id关联后取固定日期。ICU数据中同一患者有多次入科记录时按patient_id直接拉取会把多次住院的数据混在一起用目标入科记录的icu_record_id限定再以adm_time为起点做时间窗过滤得到的就是单次住院的连续数据。INTERVAL 24 HOUR这里写的是24小时如果分析窗口是入科后48小时或72小时调整这个数字即可但必须查询时用参数化写法不要固化。4.2 按事件对齐把评分记录和对应的监护数据关联起来另一个常见需求是“取每次SOFA评分时刻前1小时的平均动脉压和乳酸值”。评分表sofa_score里一行是一次评分记录score_time检验表lab_event里有乳酸值及采样时间。请求的目标是构建一张宽表每一行对应一条评分记录及该时刻前1小时内的生理参数汇总。SELECT s.icu_record_id, s.score_time, s.sofa_score, ROUND(AVG(v.map_mmhg), 2) AS mean_map_1h, AVG(v.hr_bpm) AS mean_hr_1h, l.lactate_value FROM sofa_score s LEFT JOIN vitals_min v ON v.icu_record_id s.icu_record_id AND v.record_time s.score_time - INTERVAL 60 MINUTE AND v.record_time s.score_time LEFT JOIN ( SELECT icu_record_id, sample_time, lactate_value, ROW_NUMBER() OVER ( PARTITION BY icu_record_id ORDER BY ABS(TIMESTAMPDIFF(MINUTE, sample_time, score_time)) ) AS rn FROM lab_event WHERE test_name LACTATE ) l ON l.icu_record_id s.icu_record_id AND l.rn 1 WHERE s.icu_record_id ICU2025000123 GROUP BY s.icu_record_id, s.score_time, s.sofa_score, l.lactate_value;这里有两个容易踩的细节。生命体征时间窗使用和半开区间是因为监护数据通常闭区间取整避免同一分钟数据被相邻窗口重复统计而乳酸值取的是时间上最靠近评分时刻的那一次采样而不是评分时刻前的最后一次因为检验采样时间常常比录入时间早单纯按 score_time取最后一条会漏掉更接近实际采血时刻的记录。GROUP BY中保留了score_time和sofa_score是因为这两列在子查询中已经确定带上它们不会改变结果行数但能让查询的可读性更好。4.3 按频次重采样把分钟级数据升维到小时级特征建立预测模型或做长周期趋势展示时分钟级数据往往需要统一到小时级。重采样最稳妥的做法是直接聚合而不是先查明细再在内存里循环处理。SQL的GROUP BY配合DATE_FORMAT可以把分钟级记录归约到小时桶。SELECT icu_record_id, DATE_FORMAT(record_time, %Y-%m-%d %H:00:00) AS hour_bucket, COUNT(*) AS sample_count, MIN(sbp_mmhg) AS sbp_min, MAX(sbp_mmhg) AS sbp_max, ROUND(AVG(sbp_mmhg), 2) AS sbp_mean FROM vitals_min WHERE icu_record_id ICU2025000123 AND record_time 2025-01-15 08:00:00 AND record_time 2025-01-16 08:00:00 GROUP BY icu_record_id, DATE_FORMAT(record_time, %Y-%m-%d %H:00:00) ORDER BY hour_bucket;这个查询的隐含逻辑是先按小时边界生成hour_bucket然后对落入同一个小时桶的所有分钟记录做聚合。COUNT(*) 行的统计很重要它告诉你这个小时桶里实际有多少次测量记录。ICU数据中监护仪的脱落、断网、病人转运都会造成特定时段记录骤减一个小时的记录数如果远低于该设备正常采样频率这一小时的均值可信度就低需要在特征表中额外记录sample_count作为质量标记。DATE_FORMAT里的 %H 是按24小时制注意不要写成 %h后者只有12小时制会导致下午的数据被错误叠加到上午的桶里。4.4 参数设计参考采样窗口、频次与质量标记的选择不同分析任务对数据粒度的要求差异很大常用参数可按使用场景分类分析目标推荐粒度推荐时间窗质量标记关注点入ICU早期预警评分分钟级入科后24小时每小时样本数是否低于正常值的一半血流动向趋势分析小时级入科后72小时连续缺失是否超过2小时预后模型特征小时级日级全住院期缺失比例是否超过该变量的20%设备管理分析原始精度按设备时段记录间隔是否有突变时间窗的参数不建议写死在代码里建议放在配置表或者查询入口处作为参数传入。ICU库的数据记录经常因为转科、外出检查产生分段参数设置时预留一个“允许中断时长”会让查询更稳。比如入ICU后24小时的生命体征允许因外出检查中断不超过1小时这个逻辑需要在SQL外面用脚本判断而不适合写进单条SQL里因为SQL层面难以表达“连续中断”这种序列语义。5. 常见问题与排查数据对不上、时间错位、重复记录ICU数据分析中“跑出来结果和别人不一样”是家常便饭多数时候不是计算逻辑的问题而是对表数据本身的处理有误。以下五条是该类ICU库中高频出现的数据质量坑按现象、原因、解决三步梳理。5.1 同一个患者出现多条入科记录统计时长却翻倍现象某患者住院号在ICU总时长按入科表求和后是30天但护士站记录显示实际只有15天。 原因查询时用了SUM(dis_time - adm_time)且按 patient_id 分组而该患者期间转出ICU又再次转入形成了两行入科记录。机械相加把出科期间的非ICU时间也算进去了。 解决先确认每次ICU入科的唯一标识是 icu_record_id再做时长统计时要么按单人单次入科记录分组要么先判断两次入科记录之间的间隔。若间隔小于24小时且转出科室为ICU同病区可视为一次连续性住院的中间转出转入若大于24小时再归类为两次独立事件。这个阈值需要和临床确认不能自己拍脑袋定。5.2 生命体征表出现大量负数或超生理范围的大值现象vitals_min表中某患者MAP字段出现 -20 或 300 这样的值平均值被严重拉偏。 原因监护仪在导联脱落或传感器故障时会产生极值噪声部分护士手工录入时也可能把“未测”录成占位数值。如果文档中缺失值标记是 -1负数就会被误读为有效血压。 解决建透视表前先做范围过滤器按临床常规边界值过滤。体温设为28~43摄氏度心率设为20~250次/分收缩压设为40~280mmHg。超过阈值的记录不要在原始表中删除而是在查询中打标记保留因为提取特征和做异常检测时这些噪声值本身可能需要作为质量特征放入模型。5.3 时间字段的值偏移了几十分钟导致事件关联错位现象护理记录中某患者8:00记录的GCS评分与其7:55的监护仪低血氧事件在时间上完全对不上。 原因护理评估的实际发生时间往往早于录入时间。护士通常先执行操作后补录或交班时补录前几个小时的记录。文档中如果只提供“记录生成时间”不提供“业务发生时间”两种事件对齐时差就可能放大到30分钟以上。 解决优先使用各表自带的业务时间字段如果没有检查文档中是否描述了录入延时或批量导入行为。在事件对齐时将时间窗口放宽到前后15分钟而不是取精确同一时刻。建立表间时间偏移的统计直方图能帮助你判断偏移是恒定值还是随机抖动。5.4 同一小时桶内数据点数量差异巨大现象某患者ICU第1天每小时平均有50~60个MAP样本第3天某小时只有3个。 原因监护仪导联松脱、患者外出检查、设备断电。ICU里这是正常的不是数据丢失。但如果直接用平均MAP作为全天代表值这3个样本所在的时段会被赋予过高的可信度。 解决聚合时同时保留样本数COUNT(*)并设置最低样本数阈值。例如分钟级源数据是每分钟一条时小时桶至少保留45个样本否则该小时均值在特征表中标记为NULL或置为低置信度。这样下游模型可以直接把质量标记用于缺失策略而不是在事后找原因。5.5 同一份数据在两张表中定义不同现象患者体重在admission表中记录为70在ventilator_setting表中记录为65。 原因入科体重是患者或家属主诉呼吸机上的是床秤实测值。ICU入院初期患者可能存在液体潴留体重变化本身就有临床意义不能简单合并处理。 解决不同的测量方式不能混用。做AKI评估或药物剂量计算时要定义清楚使用哪张表的哪个体重并在特征文档中留下记录。表文档中如果标注了“来源系统”这类差异大多源于源系统本身的定义不同应保留两张表各自的字段而不是强行合并到一个字段中。数据血缘记录能帮你以后回头追查这会节省大量排错时间。6. 用表间计数校验验证你对表结构的理解是否到位表结构文档读得再熟最终还是要靠数据说话。对比ICU床旁记录与库中数据是验证表结构理解是否正确的有效手段但这里不依赖人工核对而是用SQL做表间计数校验。第一步验证主表与子表的关联完整性。对任意一张子表统计其关联到主表的记录数与子表总行数之差期望差值为0。如果存在孤儿行说明ETL过程在关联主键时发生了丢失后续分析中这类记录会被漏掉特征工程阶段要决定是剔除还是人工补记。如下SQL可以快速完成这一检查SELECT count(*) AS orphan_rows FROM vitals_min v LEFT JOIN icu_admission a ON v.icu_record_id a.icu_record_id WHERE a.icu_record_id IS NULL;第二步验证时间范围的合理性。对每个icu_record_id检查其子表的最早时间是否晚于入科时间、最晚时间是否早于出科时间。若出现记录时间超出住院边界的情况通常是时钟同步问题或入科出科时间本身有误。单个小时的偏移可以通过过滤解决系统性偏移需要与设备时钟校准日志对照。第三步验证字段值的一致性。选一个关键字段如生命体征表中的平均动脉压与同一时段内的护理记录表或检验结果表做交叉对照。两张表的数值允许有合理偏差但分布特征的差异不应太大。如果差异很大大概率是其中一张表的字段定义与实际存储数据不一致文档与实际不符的情况存在不值得迷信文档。养成在每次取数任务开始前跑一遍这三步校验的习惯能省下反复在结果中找“为什么多一个数”的时间。ICU数据表和常规业务表不同字段含义经常随临床流程调整而变化文档只是静态快照数据本身才是活的。希望这份ICU数据库表的解读思路能帮你在实际项目中少走弯路把时间花在看病而不是找数上。本文还有配套的精品资源点击获取