
本文详解如何将 sql 查询结果按日期归类,并以清晰、可读的结构(如“2022-02-15 —–”后接带短横线的条目)写入 txt 文件,避免原始扁平化输出,提升导出数据的实用性与可读性。
在实际 Web 应用中,用户常需按时间维度(如某年某月)导出结构化日志、笔记或业务记录。但直接遍历查询结果并逐字段拼接(如原代码中 fwrite($fh, $item)),极易导致语义混乱、日期重复、格式僵硬等问题。要实现目标格式:
2022-02-15 ----- - lorem ipsum - dolor sit amet 2022-02-20 ----- - consectetur adipiscing elit
关键在于按日期分组 + 控制段落结构 + 安全字符串处理。以下是优化后的完整实现方案:
✅ 推荐做法:使用 while 循环 + 时间戳缓存 + 格式化拼接
$user = $sanitize->for_db($_POST['user']); $date = $_POST['date']; // 假设格式为 '2022-02'(年-月) // ✅ 更安全的日期过滤:利用 LIKE '2022-02-%' 避免 SQL 注入风险(已预处理 $user) $sql = "SELECT * FROM `table_name` WHERE `user` = ? AND `date` LIKE ? ORDER BY `date` DESC"; $stmt = $database->prepare($sql); $stmt->bind_param('ss', $user, $date . '-%'); $stmt->execute(); $result = $stmt->get_result(); if ($result->num_rows === 0) { $error[] = "Sorry, there was an error and the data was not exported."; } else { $file = 'path/to/' . $user . '.txt'; $fh = fopen($file, 'a'); if (!$fh) { $error[] = "Failed to open file for writing."; } else { $current_date = ''; // 缓存当前处理的日期(用于分组判断) $output = ''; while ($r = $result->fetch_assoc()) { $formatted_date = date('F d, Y', strtotime($r['date'])); // 如:February 15, 2022 // ✅ 检测日期变更:触发新日期标题行 if ($r['date'] !== $current_date) { if (!empty($current_date)) { $output .= "n"; // 上一组结束后空一行 } $output .= $formatted_date . " -----n"; $current_date = $r['date']; } // ✅ 添加条目(假设内容字段名为 'data';请根据实际表结构调整) $output .= "- " . trim((string)$r['data']) . "n"; } fwrite($fh, $output); fclose($fh); $success[] = "Your data has been exported successfully!"; } }
⚠️ 重要注意事项
- SQL 注入防护:原代码中直接拼接 $user 和 $period 存在严重风险。本方案改用 预处理语句(Prepared Statement),确保输入被安全绑定,杜绝注入漏洞。
- 日期字段校验:确保数据库中 date 字段为 DATE 或 DATETIME 类型,且值有效(strtotime() 对非法日期会返回 false)。建议增加容错:
$ts = strtotime($r['date']); $formatted_date = $ts ? date('F d, Y', $ts) : 'Invalid Date'; - 字段名适配:示例中使用 $r[‘data’] 作为内容字段,请根据实际数据库表结构调整(如 content、note、description 等)。
- 文件写入权限:确保 path/to/ 目录具有 Web 服务器用户(如 www-data)的写权限,否则 fopen() 将失败。
- 大数量优化:若单月数据量极大(如超万行),建议启用 ob_start() 缓冲或分块写入,避免内存溢出。
✅ 总结
实现美观、结构化的文本导出,核心不在于“怎么写文件”,而在于“如何组织数据逻辑”。通过缓存上一个日期值,在循环中动态判断是否需要插入分组标题,即可自然生成层次分明的输出。配合预处理语句与健壮的错误处理,该方案既安全可靠,又易于维护和扩展——例如后续可轻松支持 csv/jsON 导出、添加时间范围摘要,或集成 ZIP 批量打包功能。