CakePHP 4.4:如何为患者获取各笔记类型的最新笔记?
解决方案:获取每个患者各NoteType的最新笔记
单个患者的查询实现
针对单个患者,推荐用子查询+关联查询的方式,避免依赖数据库DISTINCT的不同行为,确保稳定获取各笔记类型的最新记录:
// 构建子查询:获取该患者下每个笔记类型的最新日期与笔记ID $subquery = $this->Patients->Notes->find() ->select([ 'Notes.patient_id', 'Notes.note_type_id', 'max_date' => $this->Patients->Notes->query()->func()->max('Notes.date'), 'max_id' => $this->Patients->Notes->query()->func()->max('Notes.id') ]) ->where(['Notes.patient_id' => $targetPatientId]) ->group(['Notes.patient_id', 'Notes.note_type_id']); // 主查询:关联子查询获取完整笔记与笔记类型数据 $latestNotes = $this->Patients->Notes->find() ->select(['Notes.*', 'NotesTypes.*']) ->contain(['NotesTypes']) ->innerJoin( ['Sub' => $subquery], [ 'Notes.patient_id = Sub.patient_id', 'Notes.note_type_id = Sub.note_type_id', 'Notes.date = Sub.max_date', 'Notes.id = Sub.max_id' // 处理同日期多条笔记的冲突 ] ) ->all();
如果你的数据库支持DISTINCT ON(如PostgreSQL),也可以用更简洁的写法:
$latestNotes = $this->Patients->Notes->find() ->where(['patient_id' => $targetPatientId]) ->select(['Notes.*', 'NotesTypes.*']) ->contain(['NotesTypes']) ->distinct(['Notes.note_type_id']) ->orderDesc(['Notes.date', 'Notes.id']) ->all();
批量处理所有患者
若需一次性获取所有患者的各类型最新笔记,只需调整子查询的WHERE条件,再按患者分组整理结果:
// 子查询:获取所有患者下每个笔记类型的最新记录标识 $subquery = $this->Patients->Notes->find() ->select([ 'Notes.patient_id', 'Notes.note_type_id', 'max_date' => $this->Patients->Notes->query()->func()->max('Notes.date'), 'max_id' => $this->Patients->Notes->query()->func()->max('Notes.id') ]) ->group(['Notes.patient_id', 'Notes.note_type_id']); // 主查询关联子查询 $latestNotes = $this->Patients->Notes->find() ->select(['Patients.id', 'Patients.name', 'Notes.*', 'NotesTypes.*']) ->contain(['Patients', 'NotesTypes']) ->innerJoin( ['Sub' => $subquery], [ 'Notes.patient_id = Sub.patient_id', 'Notes.note_type_id = Sub.note_type_id', 'Notes.date = Sub.max_date', 'Notes.id = Sub.max_id' ] ) ->all(); // 整理成按患者ID分组的数组 $groupedResult = []; foreach ($latestNotes as $note) { $groupedResult[$note->patient_id][$note->note_type_id] = $note; }
CakeBook相关指引要点
- 关联与Contain用法:确保
contain()正确加载关联模型,若使用select()需显式包含关联模型字段,否则会丢失关联数据。 - 聚合函数与分组:使用
func()->max()等聚合函数时,必须搭配group()子句指定分组维度,否则会导致聚合结果错误。 - 子查询的适用场景:子查询是处理「每组取最新/ oldest记录」这类需求的通用方案,避免依赖数据库特定语法。
- Limit的误区:直接在关联查询中加
limit(1)会限制整个关联结果的总数量,而非每组返回1条,这是很多测试失败的核心原因。
内容的提问来源于stack exchange,提问作者Mirko Pagliai
相关产品推荐
相关产品推荐

