挂号补单功能的完善
This commit is contained in:
284
排查指南-字段查询问题.md
Normal file
284
排查指南-字段查询问题.md
Normal file
@@ -0,0 +1,284 @@
|
||||
# 排查指南:字段查询不到数据的问题
|
||||
|
||||
## 问题类型
|
||||
**症状**: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<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,验证查询条件是否正确:
|
||||
|
||||
```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. **验证修复**:修改后验证数据
|
||||
|
||||
**记住**:查询条件使用的字段,必须在数据插入/更新时被设置!
|
||||
|
||||
|
||||
Reference in New Issue
Block a user