Files
his/排查指南-字段查询问题.md

7.8 KiB
Raw Blame History

排查指南:字段查询不到数据的问题

问题类型

症状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 方法,检查是否设置了该字段。

检查位置

  1. Service 实现类:查找 saveinsertupdate 相关方法
  2. Mapper XML:检查 INSERT/UPDATE 语句
  3. 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);

第四步:对比能查到数据的方法

找到系统中能查到数据的类似查询,对比差异。

对比维度

  1. 使用的字段start_time vs create_time
  2. 查询条件WHERE 子句的差异
  3. 关联表:是否 JOIN 了其他表
  4. 业务逻辑:是否过滤了特定状态

示例对比

-- ❌ 查不到数据的方法
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

  1. 查看表结构
  2. 查看数据样本
  3. 执行测试查询
  4. 检查字段的 NULL 值比例

预防措施

1. 代码审查检查清单

在代码审查时,检查以下内容:

  • 插入数据时:是否设置了所有必要的业务字段?
  • 查询条件时:使用的字段是否在插入时被设置?
  • 字段命名:是否遵循命名规范(create_time vs start_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
  • 解决:确保插入时设置关联字段

快速排查清单

遇到"查询不到数据"问题时,按以下顺序检查:

  1. 数据库中有数据吗?

    SELECT COUNT(*) FROM adm_encounter WHERE delete_flag = '0';
    
  2. 查询条件使用的字段有值吗?

    SELECT start_time FROM adm_encounter WHERE delete_flag = '0' LIMIT 10;
    
  3. 插入代码设置了该字段吗?

    • 查看 Service 的 save/insert 方法
    • 检查是否调用了 setter 方法
  4. 字段的业务含义是什么?

    • 是系统字段(自动设置)还是业务字段(手动设置)?
  5. 有类似功能能查到数据吗?

    • 对比能查到数据的方法,找出差异

总结

核心原则

  1. 先看数据:检查字段是否有值
  2. 再看代码:检查插入时是否设置
  3. 对比差异:找出能查到数据的方法
  4. 验证修复:修改后验证数据

记住:查询条件使用的字段,必须在数据插入/更新时被设置!