<think>我们注意到日志显示:从 daily_output_plan_final 获取到 0 条记录。
可能的原因:
1. 数据库连接问题:但日志显示已连接成功。
2. SQL脚本执行失败:在运行SQL脚本时可能出错,但日志中未捕获详细错误(需要检查日志中是否有执行SQL脚本的错误)。
3. 数据源表(work_orders, craft_data, part_category)中没有符合条件的数据。
4. SQL脚本逻辑导致没有数据插入到daily_output_plan_final。
我们已经在代码中捕获了执行SQL脚本的异常,并记录错误日志。因此,我们需要检查日志中是否有错误信息。
另外,我们注意到在运行SQL脚本之前,我们创建了表(如果不存在),然后执行整个脚本。脚本中第一步是删除表中的所有数据(DELETE FROM daily_output_plan_final;),然后插入新数据。
如果没有数据插入,可能是因为:
a) 源表(work_orders)中没有数据,或者数据被过滤掉了(WHERE条件过滤)。
b) 递归CTE(CorrectedSchedule)执行失败(例如,递归深度过大或无限循环),但SQLite默认递归深度为1000,如果超过会报错。
建议步骤:
1. 检查日志中是否有执行SQL脚本的错误(logger.error(f"执行 SQL 脚本失败: {e}", exc_info=True))。
2. 检查源表(work_orders)中是否有数据,并且满足条件:(CAST(wo.taskQty AS REAL) > 0 AND (CAST(wo.outputQty AS REAL) / CAST(wo.taskQty AS REAL)) <= 0.9)
3. 检查其他关联表(craft_data, part_category)是否有数据。
如果日志中没有错误,但就是没有数据,我们可以尝试在SQL脚本中分段调试(但代码中已经是一个完整的脚本,无法在应用内分段执行)。
替代方案:在应用内逐步执行SQL脚本的各个部分,并检查每一步是否有数据。
但是,由于我们目前无法在应用内直接调试,我们可以考虑以下方法:
1. 在数据库中手动执行SQL脚本,观察错误或中间结果。
2. 在代码中增加中间步骤的日志输出,例如记录每一步CTE的行数。
然而,由于SQL脚本很长,我们可以在代码中不这样做,而是建议先手动执行脚本。
但是,我们也可以修改SQL脚本,在每一步CTE后插入日志记录(但SQLite不支持在CTE中记录日志)。因此,我们可以将中间结果保存到临时表,然后记录行数。
不过,考虑到时间,我们先检查日志中是否有错误。如果没有错误,那么可能是没有源数据。
另外,我们发现SQL脚本中有一个条件:WHERE (CAST(wo.taskQty AS REAL) > 0 AND (CAST(wo.outputQty AS REAL) / CAST(wo.taskQty AS REAL)) <= 0.9)
这个条件会过滤掉已完成90%以上的工单。如果我们的工单都已完成超过90%,那么就不会有数据。
因此,我们需要检查work_orders表中的数据。
如何检查?我们可以在执行SQL脚本之前,先执行一个查询,检查work_orders表中符合条件的数据。
修改代码:在process_and_save_daily_output_plan方法中,执行SQL脚本之前,先检查work_orders表中是否有数据。
但是,由于我们的SQL脚本很长,我们可以先手动执行一个简单的查询。
在代码中添加一个步骤:检查源表是否有数据。
修改后的process_and_save_daily_output_plan方法:
```python
def process_and_save_daily_output_plan(self):
"""
读取 SQL 脚本并执行,生成产出计划数据到 daily_output_plan_final 表。
"""
if not os.path.exists(self.sql_script_path):
logger.error(f"SQL 脚本文件未找到: {self.sql_script_path}")
raise FileNotFoundError(f"SQL script not found at {self.sql_script_path}")
# 先检查work_orders表中是否有数据
check_query = "SELECT COUNT(*) FROM work_orders"
try:
count = self.db_manager.query(check_query)[0][0]
logger.info(f"work_orders表中有 {count} 条记录。")
except Exception as e:
logger.error(f"检查work_orders表失败: {e}", exc_info=True)
return False
# 再检查是否有满足条件的工单
condition_query = """
SELECT COUNT(*)
FROM work_orders
WHERE (CAST(taskQty AS REAL) > 0)
AND (CAST(outputQty AS REAL) / CAST(taskQty AS REAL) <= 0.9)
"""
try:
condition_count = self.db_manager.query(condition_query)[0][0]
logger.info(f"work_orders表中有 {condition_count} 条满足条件的记录(未完成90%以上)。")
except Exception as e:
logger.error(f"检查满足条件的工单失败: {e}", exc_info=True)
return False
try:
with open(self.sql_script_path, 'r', encoding='utf-8') as f:
sql_script = f.read()
logger.info(f"开始执行 SQL 脚本: {self.sql_script_path}")
self.db_manager.execute_script(sql_script)
logger.info("产出计划数据已成功生成并保存到 daily_output_plan_final 表。")
return True
except Exception as e:
logger.error(f"执行 SQL 脚本失败: {e}", exc_info=True)
return False
```
这样,我们可以在日志中看到源表的数据情况。
另外,我们还需要检查SQL脚本中其他部分,比如关联表(craft_data)是否有数据?如果关联不上,那么production_time可能为0,导致后续计算错误。
我们可以在PreparedData CTE中增加日志?但不行,因为是在同一个脚本中。
因此,我们可以在应用外手动执行整个SQL脚本,观察每一步的中间结果。
但是,由于应用已经集成,我们可以在代码中捕获SQL脚本执行时的异常,并打印出具体错误。
目前,我们的execute_script方法可能没有返回详细的错误信息。我们需要确保DatabaseManager中的方法能够抛出异常,并且我们在这里捕获并记录。
我们已经捕获了异常并记录,所以如果SQL脚本执行出错,我们会在日志中看到。
所以,请检查日志中是否有错误信息。
如果没有错误信息,但是也没有数据,那么可能是条件过滤导致没有数据插入。
另外,我们注意到在PreparedData中,我们使用了LEFT JOIN craft_data,如果找不到craft_data,则使用默认值。所以即使craft_data没有数据,也会使用默认值。
因此,我们首先检查work_orders表中是否有满足条件的数据。
如果满足条件的数据为0,那么我们需要调整条件或者导入数据。
如果满足条件的数据大于0,那么可能是SQL脚本中的递归CTE或其他部分导致没有数据。
由于递归CTE比较复杂,我们可以在数据库管理工具中手动执行整个SQL脚本,并逐步调试。
总结:我们在代码中添加了对源表数据的检查,这样可以帮助我们确定问题是否出在源数据上。
请尝试添加上述检查,并重新运行,查看日志输出。
另外,确保数据库中的work_orders表有数据,并且有满足条件(未完成90%以上)的工单。
如果问题仍然存在,请手动执行SQL脚本,观察执行情况。
手动执行步骤:
1. 用数据库工具(如DB Browser for SQLite)打开数据库文件。
2. 复制SQL脚本内容,执行整个脚本。
3. 观察是否有错误,或者查看每一步的中间结果(可以将中间CTE的结果插入临时表以便查看)。
由于SQL脚本很长,手动执行可能会遇到性能问题,但可以分段执行(例如,先执行到PreparedData,然后查询PreparedData的数据)。
如何分段?可以在PreparedData后面插入一条语句,将PreparedData的结果存入临时表,然后查询该临时表。
例如:
```sql
WITH PreparedData AS (...),
-- 将PreparedData的结果存入临时表
CREATE TEMP TABLE TempPreparedData AS SELECT * FROM PreparedData;
```
然后查询TempPreparedData,看是否有数据。
同样,可以检查RankedData,CorrectedSchedule等。
这样逐步定位问题所在。
由于我们无法远程调试,以上方法需要本地进行。
在代码中,我们已经添加了对源数据的检查,这样至少可以知道是不是因为源数据不足导致。
请先按照上述修改代码,然后重新运行,查看日志。
如果源数据满足条件,但SQL脚本执行后还是没有数据,那么我们需要进一步调试SQL脚本。
另外,注意SQL脚本中递归CTE的终止条件:它依赖于global_rank的连续递增。如果global_rank不是从1开始连续递增,递归可能提前终止。
在RankedData中,我们使用ROW_NUMBER()生成global_rank,所以应该是连续的。但是,如果RankedData没有数据,那么递归CTE就不会有数据。
因此,我们首先确保PreparedData和RankedData有数据。
修改后的代码中,我们已经检查了work_orders中满足条件的数据,那么如果满足条件的数据>0,但RankedData中没有数据,可能是其他原因(如关联表、计算字段等)。
我们可以在PreparedData中检查是否有数据,通过临时表的方式(在应用中不便于做,所以建议手动执行)。
综上所述,我们先修改代码,添加对源数据的检查,然后根据日志输出再作判断。
修改后的OutputPlanModel代码:</think>您的代码显示数据库连接成功,但查询结果为空。这通常由以下几个原因导致:
### 问题分析
1. **数据源表为空**:
- `work_orders` 表可能没有数据
- `craft_data` 表可能没有数据
- `part_category` 表可能没有数据
2. **SQL脚本执行失败**:
- 脚本可能未正确执行
- 脚本中的过滤条件可能过滤掉了所有数据
3. **数据过滤条件**:
- `WHERE (CAST(wo.taskQty AS REAL) > 0 AND (CAST(wo.outputQty AS REAL) / CAST(wo.taskQty AS REAL)) <= 0.9` 可能过滤了所有记录
4. **递归CTE问题**:
- 递归查询可能因数据问题无法完成
### 解决方案
#### 1. 添加数据验证和调试日志
在 `process_and_save_daily_output_plan` 方法中添加详细日志:
```python
def process_and_save_daily_output_plan(self):
# ... [原有代码] ...
try:
# 添加数据验证
logger.info("检查源表数据状态...")
self._log_table_status("work_orders")
self._log_table_status("craft_data")
self._log_table_status("part_category")
# 检查是否有满足条件的工单
condition_check = """
SELECT COUNT(*)
FROM work_orders
WHERE (CAST(taskQty AS REAL) > 0)
AND (CAST(outputQty AS REAL) / CAST(taskQty AS REAL) <= 0.9
"""
valid_count = self.db_manager.query(condition_check)[0][0]
logger.info(f"满足条件的工单数量: {valid_count}")
# 执行SQL脚本
with open(self.sql_script_path, 'r', encoding='utf-8') as f:
sql_script = f.read()
logger.info(f"开始执行 SQL 脚本: {self.sql_script_path}")
self.db_manager.execute_script(sql_script)
# 检查执行结果
result_check = "SELECT COUNT(*) FROM daily_output_plan_final"
result_count = self.db_manager.query(result_check)[0][0]
logger.info(f"daily_output_plan_final 表记录数: {result_count}")
logger.info("产出计划数据已成功生成并保存到 daily_output_plan_final 表。")
return True
except Exception as e:
logger.error(f"执行 SQL 脚本失败: {e}", exc_info=True)
return False
def _log_table_status(self, table_name):
"""记录表的状态信息"""
try:
# 检查表是否存在
check_table = f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}'"
exists = self.db_manager.query(check_table)
if not exists:
logger.warning(f"表 {table_name} 不存在")
return
# 获取记录数
count_query = f"SELECT COUNT(*) FROM {table_name}"
count = self.db_manager.query(count_query)[0][0]
# 获取列信息
columns_query = f"PRAGMA table_info({table_name})"
columns = [col[1] for col in self.db_manager.query(columns_query)]
logger.info(f"表 {table_name} - 记录数: {count}, 列: {', '.join(columns)}")
# 记录前5行数据样本
if count > 0:
sample_query = f"SELECT * FROM {table_name} LIMIT 5"
sample = self.db_manager.query(sample_query)
logger.info(f"表 {table_name} 数据样本: {sample}")
except Exception as e:
logger.error(f"检查表 {table_name} 状态失败: {e}")
```
#### 2. 添加SQL脚本调试功能
创建脚本调试方法:
```python
def debug_sql_script(self):
"""分步执行SQL脚本进行调试"""
try:
with open(self.sql_script_path, 'r', encoding='utf-8') as f:
sql_script = f.read()
# 分割SQL脚本为独立的语句
statements = [stmt.strip() for stmt in sql_script.split(';') if stmt.strip()]
logger.info(f"开始分步调试SQL脚本,共 {len(statements)} 条语句")
for i, stmt in enumerate(statements, 1):
try:
logger.info(f"执行语句 {i}/{len(statements)}: {stmt[:100]}...")
# 执行当前语句
self.db_manager.execute(stmt)
# 如果是CTE查询,尝试获取中间结果
if "WITH" in stmt.upper() and "SELECT" in stmt.upper():
cte_name = stmt.split("AS")[0].split()[-1]
if cte_name:
try:
sample = self.db_manager.query(f"SELECT * FROM {cte_name} LIMIT 5")
logger.info(f"中间表 {cte_name} 样本数据: {sample}")
except:
pass
logger.info(f"语句 {i} 执行成功")
except Exception as e:
logger.error(f"执行语句 {i} 失败: {e}")
logger.error(f"失败语句: {stmt}")
return False
return True
except Exception as e:
logger.error(f"调试SQL脚本失败: {e}")
return False
```
#### 3. 在调用代码中添加调试入口
在您的应用程序入口点添加调试选项:
```python
# 在调用OutputPlanModel的地方
output_model = OutputPlanModel(db_manager)
# 调试模式
if DEBUG_MODE: # 设置一个调试标志
# 检查表状态
output_model.process_and_save_daily_output_plan()
# 分步调试SQL脚本
output_model.debug_sql_script()
# 获取数据
data = output_model.get_output_plan_data()
print(f"获取到的数据: {data}")
else:
# 正常执行
output_model.process_and_save_daily_output_plan()
data = output_model.get_output_plan_data()
```
### 常见问题解决方案
1. **源表无数据**:
```python
# 模拟插入测试数据
test_data = """
INSERT INTO work_orders (prodplanId, workOrderNo, lineName, itemNo, itemName, taskQty, outputQty, scheduleStartDate, scheduleEndDate, actualStartDate, craftSection, order_index, remark, externalType, factoryName)
VALUES
('BATCH-001', 'WO-001', 'Line1', 'ITEM-001', 'Test Item', 1000, 200, '2023-01-01 08:00:00', '2023-01-05 17:00:00', '2023-01-01 08:00:00', 'Assembly', 1, 1, 'TypeA', 'FactoryA'),
('BATCH-001', 'WO-002', 'Line1', 'ITEM-001', 'Test Item', 1000, 200, '2023-01-01 08:00:00', '2023-01-05 17:00:00', '2023-01-01 08:00:00', 'Test', 2, 2, 'TypeA', 'FactoryA');
"""
db_manager.execute_script(test_data)
```
2. **SQL脚本执行失败**:
- 使用 `debug_sql_script()` 方法分步执行和调试
- 检查SQLite版本是否支持所有使用的功能(窗口函数、CTE等)
3. **数据库连接问题**:
```python
# 在DatabaseManager中添加连接验证
def verify_connection(self):
try:
result = self.query("SELECT 1")
return result[0][0] == 1
except Exception as e:
logger.error(f"数据库连接验证失败: {e}")
return False
```
### 最终建议
1. 启用调试模式运行应用程序
2. 检查日志中的表状态信息
3. 使用 `debug_sql_script()` 分步执行SQL脚本
4. 根据日志信息添加测试数据或修复SQL脚本