Files
012-kaopeilian/知识库/知识点删除问题分析.md
111 998211c483 feat: 初始化考培练系统项目
- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
2026-01-24 19:33:28 +08:00

379 lines
9.3 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 知识点DELETE语句未能删除全部记录的原因分析
**问题SQL**
```sql
DELETE FROM knowledge_points WHERE material_id = 34;
```
**问题描述**用户反馈该SQL语句未能删除资料34的全部知识点
---
## 🔍 问题分析
### 1. 数据现状
资料34当前有6个知识点记录
| ID | 知识点名称 | is_deleted | 创建时间 |
|----|-----------|------------|----------|
| 1142 | FAB法则介绍 | 0 | 2025-10-17 07:20:28 |
| 1143 | 全面有效的抗衰需求满足 | 0 | 2025-10-17 07:20:28 |
| 1144 | 能量聚焦与精准技术 | 0 | 2025-10-17 07:20:28 |
| 1145 | 多层次治疗与专利炮头 | 0 | 2025-10-17 07:20:28 |
| 1146 | 国内首款医美合规超声刀的安全性与合规性 | 0 | 2025-10-17 07:20:28 |
| 1147 | 提升舒适度与减少副作用 | 0 | 2025-10-17 07:20:28 |
### 2. 表结构分析
```sql
CREATE TABLE `knowledge_points` (
`id` int NOT NULL AUTO_INCREMENT,
`course_id` int DEFAULT NULL COMMENT '所属课程ID',
`material_id` int NOT NULL COMMENT '关联资料ID必填',
`name` varchar(200) NOT NULL COMMENT '知识点名称',
`description` text COMMENT '知识点描述',
`is_deleted` tinyint(1) DEFAULT '0' COMMENT '是否删除', -- ⚠️ 软删除字段
`deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
`created_by` int DEFAULT NULL,
`updated_by` int DEFAULT NULL,
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`type` varchar(50) DEFAULT '理论知识',
`source` tinyint(1) DEFAULT '0',
`topic_relation` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_course_id` (`course_id`),
KEY `idx_is_deleted` (`is_deleted`),
KEY `material_id` (`material_id`),
CONSTRAINT `knowledge_points_ibfk_1` FOREIGN KEY (`course_id`)
REFERENCES `courses` (`id`) ON DELETE CASCADE,
CONSTRAINT `knowledge_points_ibfk_2` FOREIGN KEY (`material_id`)
REFERENCES `course_materials` (`id`) ON DELETE CASCADE
);
```
**关键发现**
- ✅ 表中有 `is_deleted` 字段 → **系统使用软删除机制**
- ✅ material_id有外键约束ON DELETE CASCADE
- ✅ course_id有外键约束ON DELETE CASCADE
### 3. 外键约束检查
```sql
-- exam_mistakes表引用knowledge_points
CONSTRAINT exam_mistakes_ibfk_4
FOREIGN KEY (knowledge_point_id)
REFERENCES knowledge_points(id)
ON DELETE SET NULL
```
**检查结果**
- ✅ 没有错题记录引用资料34的知识点
- ✅ DELETE_RULE是SET NULL不会阻止删除
---
## ❌ 问题根因
### 核心问题系统使用软删除但SQL使用了物理删除
**系统设计**
- 后端代码使用 `soft_delete()` 方法,将 `is_deleted` 设置为1
- 前端查询时使用 `WHERE is_deleted = 0` 筛选未删除的记录
- 所有删除操作都是软删除,不会真正从数据库删除记录
**用户执行的SQL**
```sql
DELETE FROM knowledge_points WHERE material_id = 34; -- ❌ 物理删除
```
**为什么"未能删除全部知识点"**
有以下几种可能原因:
#### 原因1SQL实际上执行成功了最可能
- ✅ DELETE语句执行成功记录被物理删除
- ✅ 但之后系统又重新创建了这些知识点
- 🔍 需要检查是否有后台任务或自动同步机制
#### 原因2未提交事务
```sql
-- 如果在事务中执行但未提交
START TRANSACTION;
DELETE FROM knowledge_points WHERE material_id = 34;
-- 未执行 COMMIT
```
#### 原因3没有执行权限
- 用户可能没有DELETE权限
- 需要检查用户权限
#### 原因4有触发器或其他机制
- 可能有BEFORE DELETE或AFTER DELETE触发器阻止删除
- 需要检查触发器
---
## ✅ 正确的删除方法
### 方法1使用系统的软删除推荐
**后端API方式**
```bash
# 通过后端API删除软删除
curl -X DELETE "https://aiedu.ireborn.com.cn/api/v1/courses/materials/34/knowledge-points/{knowledge_point_id}" \
-H "Authorization: Bearer {token}"
```
**直接SQL方式软删除**
```sql
-- 软删除资料34的所有知识点
UPDATE knowledge_points
SET is_deleted = 1,
deleted_at = NOW()
WHERE material_id = 34
AND is_deleted = 0;
```
### 方法2删除资料级联删除知识点
```sql
-- 软删除资料,知识点会通过外键级联删除
UPDATE course_materials
SET is_deleted = 1,
deleted_at = NOW()
WHERE id = 34
AND is_deleted = 0;
```
### 方法3物理删除谨慎使用
**⚠️ 警告**:物理删除会永久删除数据,无法恢复!
```sql
-- 如果确实需要物理删除
DELETE FROM knowledge_points WHERE material_id = 34;
-- 验证删除结果
SELECT COUNT(*) FROM knowledge_points WHERE material_id = 34;
-- 应该返回 0
```
---
## 🔍 排查步骤
### 1. 检查记录是否真的还在
```sql
-- 查看记录数量
SELECT COUNT(*) as total_count
FROM knowledge_points
WHERE material_id = 34;
-- 查看详细记录
SELECT id, name, is_deleted, deleted_at
FROM knowledge_points
WHERE material_id = 34
ORDER BY id;
```
### 2. 检查事务状态
```sql
-- 查看当前未提交的事务
SELECT * FROM information_schema.INNODB_TRX;
-- 如果有未提交的事务,提交或回滚
COMMIT;
-- 或
ROLLBACK;
```
### 3. 检查触发器
```sql
-- 查看knowledge_points表的触发器
SHOW TRIGGERS LIKE 'knowledge_points';
```
### 4. 检查删除权限
```sql
-- 查看当前用户权限
SHOW GRANTS FOR 'root'@'%';
-- 或查看当前会话权限
SHOW GRANTS FOR CURRENT_USER;
```
### 5. 验证外键约束
```sql
-- 禁用外键检查(谨慎使用)
SET FOREIGN_KEY_CHECKS = 0;
-- 执行删除
DELETE FROM knowledge_points WHERE material_id = 34;
-- 重新启用外键检查
SET FOREIGN_KEY_CHECKS = 1;
-- 验证结果
SELECT COUNT(*) FROM knowledge_points WHERE material_id = 34;
```
---
## 📝 推荐操作流程
### 场景A想要"隐藏"知识点(软删除)✅ 推荐
```sql
-- 1. 软删除知识点
UPDATE knowledge_points
SET is_deleted = 1,
deleted_at = NOW(),
updated_by = {user_id} -- 记录操作人
WHERE material_id = 34
AND is_deleted = 0;
-- 2. 验证结果
SELECT COUNT(*) as deleted_count
FROM knowledge_points
WHERE material_id = 34
AND is_deleted = 1;
-- 3. 前端查询时会自动过滤
-- SELECT * FROM knowledge_points WHERE material_id = 34 AND is_deleted = 0;
-- 返回 0 条记录
```
### 场景B需要永久删除记录物理删除 谨慎
```sql
-- 1. 备份数据(重要!)
CREATE TABLE knowledge_points_backup_20251017 AS
SELECT * FROM knowledge_points WHERE material_id = 34;
-- 2. 检查依赖关系
SELECT COUNT(*) as dependent_count
FROM exam_mistakes
WHERE knowledge_point_id IN (
SELECT id FROM knowledge_points WHERE material_id = 34
);
-- 3. 执行物理删除
DELETE FROM knowledge_points WHERE material_id = 34;
-- 4. 验证结果
SELECT COUNT(*) FROM knowledge_points WHERE material_id = 34;
-- 应该返回 0
-- 5. 提交事务
COMMIT;
```
---
## 🎯 解决方案总结
### 如果要删除资料34的全部知识点
**✅ 推荐通过后端API删除资料**
```bash
# 这会软删除资料及其所有知识点
curl -X DELETE "https://aiedu.ireborn.com.cn/api/v1/courses/{course_id}/materials/34" \
-H "Authorization: Bearer {token}"
```
**✅ 次选SQL软删除**
```sql
-- 软删除所有知识点
UPDATE knowledge_points
SET is_deleted = 1, deleted_at = NOW()
WHERE material_id = 34 AND is_deleted = 0;
```
**⚠️ 不推荐SQL物理删除**
```sql
-- 永久删除(不可恢复)
DELETE FROM knowledge_points WHERE material_id = 34;
COMMIT; -- 记得提交事务!
```
---
## 🔧 故障排除
### 如果DELETE语句执行后记录仍然存在
1. **检查事务是否提交**
```sql
COMMIT;
```
2. **检查是否在错误的数据库**
```sql
SELECT DATABASE();
-- 应该返回 kaopeilian
```
3. **检查WHERE条件是否正确**
```sql
SELECT COUNT(*) FROM knowledge_points WHERE material_id = 34;
-- 执行DELETE前应该有记录
```
4. **查看MySQL错误日志**
```bash
docker exec kaopeilian-mysql-dev tail -100 /var/log/mysql/error.log
```
5. **测试简单的DELETE**
```sql
-- 删除单条记录测试
DELETE FROM knowledge_points WHERE id = 1142 LIMIT 1;
SELECT * FROM knowledge_points WHERE id = 1142;
```
---
## 📚 相关文档
- 数据库架构:`/root/aiedu/kaopeilian-backend/数据库架构-统一版.md`
- Service层代码`/root/aiedu/kaopeilian-backend/app/services/course_service.py`
- Base Service`/root/aiedu/kaopeilian-backend/app/services/base_service.py`
---
## 💡 经验教训
1. **理解系统的删除机制**
- 软删除系统中直接使用DELETE可能导致数据不一致
- 应该使用系统提供的API或软删除SQL
2. **谨慎使用物理删除**
- 物理删除是不可逆的
- 删除前应该备份数据
- 检查是否有外键依赖
3. **使用事务**
- 重要的删除操作应该在事务中进行
- 先验证后提交
4. **记录操作日志**
- 删除操作应该记录操作人和操作时间
- 便于追溯和审计
---
**结论**用户的DELETE语句从语法上是正确的能够执行。如果执行后记录仍然存在最可能的原因是
1. 事务未提交
2. 后台任务重新创建了记录
3. 在错误的数据库环境执行
建议使用软删除而不是物理删除,以保持系统一致性。