# 排查指南:字段查询不到数据的问题 ## 问题类型 **症状**:SQL 查询条件使用了某个字段,但查询结果为空,而数据库中明明有数据。 **根本原因**:查询条件使用的字段在数据插入/更新时没有被设置(为 NULL 或默认值)。 --- ## 排查步骤 ### 第一步:确认字段是否存在 ```sql -- 检查表结构,确认字段是否存在 SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = 'adm_encounter' AND column_name = 'start_time'; ``` **检查点**: - ✅ 字段确实存在 - ❌ 字段不存在 → 检查字段名拼写、大小写 --- ### 第二步:检查字段是否有值 ```sql -- 检查字段的实际值 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 实现类**:查找 `save`、`insert`、`update` 相关方法 2. **Mapper XML**:检查 INSERT/UPDATE 语句 3. **Entity 类**:检查字段定义和默认值 **示例检查**: ```java // ❌ 错误示例:没有设置 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. **业务逻辑**:是否过滤了特定状态 **示例对比**: ```sql -- ❌ 查不到数据的方法 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: ```java @Override public IPage getTodayOutpatientPatients(...) { // 打印查询条件 log.info("查询条件: queryDate={}, doctorId={}, departmentId={}", queryDate, doctorId, departmentId); // 执行查询 IPage result = mapper.getTodayOutpatientPatients(...); // 打印结果 log.info("查询结果: 总数={}, 记录数={}", result.getTotal(), result.getRecords().size()); return result; } ``` ### 技巧2:直接执行 SQL 验证 在数据库客户端直接执行 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. 单元测试 编写单元测试,验证字段设置: ```java @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 值: ```sql -- 如果 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. 代码规范 建立代码规范,明确字段使用规则: ```java /** * 保存就诊信息(门诊挂号) * * 注意: * - 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. ✅ **数据库中有数据吗?** ```sql SELECT COUNT(*) FROM adm_encounter WHERE delete_flag = '0'; ``` 2. ✅ **查询条件使用的字段有值吗?** ```sql SELECT start_time FROM adm_encounter WHERE delete_flag = '0' LIMIT 10; ``` 3. ✅ **插入代码设置了该字段吗?** - 查看 Service 的 save/insert 方法 - 检查是否调用了 setter 方法 4. ✅ **字段的业务含义是什么?** - 是系统字段(自动设置)还是业务字段(手动设置)? 5. ✅ **有类似功能能查到数据吗?** - 对比能查到数据的方法,找出差异 --- ## 总结 **核心原则**: 1. **先看数据**:检查字段是否有值 2. **再看代码**:检查插入时是否设置 3. **对比差异**:找出能查到数据的方法 4. **验证修复**:修改后验证数据 **记住**:查询条件使用的字段,必须在数据插入/更新时被设置!