7.8 KiB
7.8 KiB
排查指南:字段查询不到数据的问题
问题类型
症状:SQL 查询条件使用了某个字段,但查询结果为空,而数据库中明明有数据。
根本原因:查询条件使用的字段在数据插入/更新时没有被设置(为 NULL 或默认值)。
排查步骤
第一步:确认字段是否存在
-- 检查表结构,确认字段是否存在
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'adm_encounter'
AND column_name = 'start_time';
检查点:
- ✅ 字段确实存在
- ❌ 字段不存在 → 检查字段名拼写、大小写
第二步:检查字段是否有值
-- 检查字段的实际值
SELECT
id,
start_time, -- 检查这个字段
create_time, -- 对比其他时间字段
status_enum
FROM adm_encounter
WHERE delete_flag = '0'
AND create_time::DATE = CURRENT_DATE -- 用能查到数据的条件
LIMIT 10;
检查点:
- ✅
start_time有值 → 继续排查查询条件 - ❌
start_time为 NULL → 问题确认:字段没有被设置
第三步:检查插入/更新代码
找到创建/更新数据的 Service 方法,检查是否设置了该字段。
检查位置:
- Service 实现类:查找
save、insert、update相关方法 - Mapper XML:检查 INSERT/UPDATE 语句
- Entity 类:检查字段定义和默认值
示例检查:
// ❌ 错误示例:没有设置 start_time
Encounter encounter = new Encounter();
encounter.setBusNo("xxx");
encounter.setPatientId(123L);
// 没有 encounter.setStartTime(...)
iEncounterService.save(encounter);
// ✅ 正确示例:设置了 start_time
Encounter encounter = new Encounter();
encounter.setBusNo("xxx");
encounter.setPatientId(123L);
encounter.setStartTime(new Date()); // 设置了字段
iEncounterService.save(encounter);
第四步:对比能查到数据的方法
找到系统中能查到数据的类似查询,对比差异。
对比维度:
- 使用的字段:
start_timevscreate_time - 查询条件:WHERE 子句的差异
- 关联表:是否 JOIN 了其他表
- 业务逻辑:是否过滤了特定状态
示例对比:
-- ❌ 查不到数据的方法
SELECT * FROM adm_encounter
WHERE start_time::DATE = CURRENT_DATE; -- start_time 为 NULL
-- ✅ 能查到数据的方法
SELECT * FROM adm_encounter
WHERE create_time::DATE = CURRENT_DATE; -- create_time 有值
第五步:检查字段的业务含义
确认字段的业务含义和使用场景。
常见情况:
create_time:记录创建时间(自动设置)update_time:记录更新时间(自动设置)start_time:业务开始时间(需要手动设置)end_time:业务结束时间(需要手动设置)
判断规则:
- 如果字段是业务字段(如
start_time),需要手动设置 - 如果字段是系统字段(如
create_time),通常自动设置
调试技巧
技巧1:打印 SQL 和参数
在 Service 方法中添加日志,查看实际执行的 SQL:
@Override
public IPage<TodayOutpatientPatientDto> getTodayOutpatientPatients(...) {
// 打印查询条件
log.info("查询条件: queryDate={}, doctorId={}, departmentId={}",
queryDate, doctorId, departmentId);
// 执行查询
IPage<TodayOutpatientPatientDto> result = mapper.getTodayOutpatientPatients(...);
// 打印结果
log.info("查询结果: 总数={}, 记录数={}",
result.getTotal(), result.getRecords().size());
return result;
}
技巧2:直接执行 SQL 验证
在数据库客户端直接执行 SQL,验证查询条件是否正确:
-- 1. 先查看数据
SELECT id, start_time, create_time, status_enum
FROM adm_encounter
WHERE delete_flag = '0'
LIMIT 10;
-- 2. 测试查询条件
SELECT COUNT(*)
FROM adm_encounter
WHERE delete_flag = '0'
AND start_time::DATE = CURRENT_DATE; -- 测试这个条件
-- 3. 对比其他条件
SELECT COUNT(*)
FROM adm_encounter
WHERE delete_flag = '0'
AND create_time::DATE = CURRENT_DATE; -- 对比这个条件
技巧3:使用数据库工具检查
使用数据库管理工具(如 pgAdmin、DBeaver):
- 查看表结构
- 查看数据样本
- 执行测试查询
- 检查字段的 NULL 值比例
预防措施
1. 代码审查检查清单
在代码审查时,检查以下内容:
- 插入数据时:是否设置了所有必要的业务字段?
- 查询条件时:使用的字段是否在插入时被设置?
- 字段命名:是否遵循命名规范(
create_timevsstart_time)? - 文档注释:字段的业务含义是否清晰?
2. 单元测试
编写单元测试,验证字段设置:
@Test
public void testSaveEncounter() {
Encounter encounter = new Encounter();
encounter.setPatientId(123L);
encounter.setBusNo("TEST001");
// 检查是否设置了 start_time
assertNotNull(encounter.getStartTime(), "start_time 应该被设置");
Long id = encounterService.saveEncounterByRegister(encounter);
// 验证数据库中的值
Encounter saved = encounterService.getById(id);
assertNotNull(saved.getStartTime(), "数据库中的 start_time 不应该为 NULL");
}
3. 数据库约束
在数据库层面添加约束,防止 NULL 值:
-- 如果 start_time 是必填字段,添加 NOT NULL 约束
ALTER TABLE adm_encounter
ALTER COLUMN start_time SET NOT NULL;
-- 或者添加默认值
ALTER TABLE adm_encounter
ALTER COLUMN start_time SET DEFAULT CURRENT_TIMESTAMP;
4. 代码规范
建立代码规范,明确字段使用规则:
/**
* 保存就诊信息(门诊挂号)
*
* 注意:
* - create_time: 自动设置(系统字段)
* - start_time: 需要手动设置(业务字段,表示就诊开始时间)
* - 如果 start_time 为 NULL,查询时使用 create_time 作为挂号时间
*/
public Long saveEncounterByRegister(Encounter encounter) {
// 如果没有设置 start_time,使用当前时间
if (encounter.getStartTime() == null) {
encounter.setStartTime(new Date());
}
// ...
}
常见问题模式
模式1:时间字段混淆
- 问题:使用
start_time查询,但插入时没有设置 - 解决:使用
create_time或确保插入时设置start_time
模式2:状态字段未设置
- 问题:使用
status_enum查询,但插入时使用默认值 - 解决:明确设置状态值
模式3:关联字段未设置
- 问题:使用
organization_id查询,但插入时为 NULL - 解决:确保插入时设置关联字段
快速排查清单
遇到"查询不到数据"问题时,按以下顺序检查:
-
✅ 数据库中有数据吗?
SELECT COUNT(*) FROM adm_encounter WHERE delete_flag = '0'; -
✅ 查询条件使用的字段有值吗?
SELECT start_time FROM adm_encounter WHERE delete_flag = '0' LIMIT 10; -
✅ 插入代码设置了该字段吗?
- 查看 Service 的 save/insert 方法
- 检查是否调用了 setter 方法
-
✅ 字段的业务含义是什么?
- 是系统字段(自动设置)还是业务字段(手动设置)?
-
✅ 有类似功能能查到数据吗?
- 对比能查到数据的方法,找出差异
总结
核心原则:
- 先看数据:检查字段是否有值
- 再看代码:检查插入时是否设置
- 对比差异:找出能查到数据的方法
- 验证修复:修改后验证数据
记住:查询条件使用的字段,必须在数据插入/更新时被设置!