C#.NET SQL Server WinForm如何导出全量查询结果为PDF表格
问题说明
在.NET C# 搭配 SQL Server 开发Windows桌面应用实现PDF导出功能时,初始编写的按钮点击代码运行后仅能导出SQL查询结果的第一行数据,仅输出单条记录的日期、总收入、总支出值,无法获取全部返回行。预期效果为生成带标准表格的PDF,表头包含date、Income、Expenses三列,逐行展示所有日期对应的收入、支出汇总数据。
初始实现代码如下:
private void button1_Click(object sender, EventArgs e) { Document doc = new Document(); PdfWriter.GetInstance(doc, new FileStream("myPdf.pdf",FileMode.Create)); doc.Open(); SqlConnection con = new SqlConnection(connectionString); if (con.State == ConnectionState.Closed) { con.Open(); } SqlCommand cmd = new SqlCommand("SELECT b.date,a.TotalIncome,b.TotalExpense FROM (SELECT c.date, sum(c.amout) as TotalIncome FROM (SELECT name FROM category_tbl where type = 'Income') a, transactions c where c.type = a.name group by c.date) a, (SELECT c.date, sum(c.amout) as TotalExpense FROM (SELECT name FROM category_tbl where type = 'Expense') a, transactions c where c.type = a.name group by c.date) b", con); SqlDataReader dr = cmd.ExecuteReader(); if (dr.Read()) { Paragraph p1 = new Paragraph(dr.GetValue(0).ToString()); Paragraph p2 = new Paragraph(dr.GetValue(1).ToString()); Paragraph p3 = new Paragraph(dr.GetValue(2).ToString()); doc.Add(p1); doc.Add(p2); doc.Add(p3); doc.Close(); MessageBox.Show("PDF create"); } }
问题根因
- 代码使用
if (dr.Read())做单次读取判断,仅会加载SqlDataReader返回的第一行数据,没有遍历全部结果集 - 原代码直接用段落拼接文本,没有使用PDF库的表格组件构建结构化表格
- 原SQL语句两个日期汇总子查询缺少关联条件,会产生笛卡尔积导致数据错误;同时
doc.Close()写在单次读取的判断块内,即使遍历后续行也无法写入文档 - 数据库连接、文件流等非托管资源没有做释放处理,容易引发文件占用、内存泄漏问题
修改后完整实现
代码基于当前使用的iText 5(iTextSharp)编写,其他版本PDF库API存在差异
private void button1_Click(object sender, EventArgs e) { // 用using包裹所有实现IDisposable的对象,自动释放资源 using (Document doc = new Document()) { PdfWriter.GetInstance(doc, new FileStream("myPdf.pdf", FileMode.Create)); doc.Open(); // 创建3列的PDF表格 PdfPTable table = new PdfPTable(3); // 设置表格宽度占页面宽度100% table.WidthPercentage = 100f; // 添加表头,设置字体加粗 Font headFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 12); table.AddCell(new PdfPCell(new Paragraph("date", headFont))); table.AddCell(new PdfPCell(new Paragraph("Income", headFont))); table.AddCell(new PdfPCell(new Paragraph("Expenses", headFont))); using (SqlConnection con = new SqlConnection(connectionString)) { con.Open(); // 修正原SQL:添加两表日期关联条件,用全连接避免单日只有收入/支出时数据丢失,空值补0 string sql = @"SELECT ISNULL(a.date, b.date) as date, ISNULL(a.TotalIncome,0) as TotalIncome, ISNULL(b.TotalExpense,0) as TotalExpense FROM (SELECT c.date, sum(c.amout) as TotalIncome FROM category_tbl a JOIN transactions c ON c.type = a.name WHERE a.type = 'Income' GROUP BY c.date) a FULL JOIN (SELECT c.date, sum(c.amout) as TotalExpense FROM category_tbl a JOIN transactions c ON c.type = a.name WHERE a.type = 'Expense' GROUP BY c.date) b ON a.date = b.date ORDER BY ISNULL(a.date, b.date)"; SqlCommand cmd = new SqlCommand(sql, con); using (SqlDataReader dr = cmd.ExecuteReader()) { // 循环读取所有行,替换原来的if单次判断 while (dr.Read()) { // 逐行把数据加入表格单元格 table.AddCell(dr["date"].ToString()); table.AddCell(dr["TotalIncome"].ToString()); table.AddCell(dr["TotalExpense"].ToString()); } } } // 把整个表格加入PDF文档 doc.Add(table); // 文档关闭操作移到循环外,确保所有内容写入完成后再关闭 doc.Close(); MessageBox.Show("PDF create"); } }
关键改动说明
- 把单次
if (dr.Read())替换为while (dr.Read())循环,遍历查询返回的所有结果行 - 引入
PdfPTable组件创建3列表格,单独添加加粗表头,所有数据逐行写入表格单元格,最终统一将表格加入PDF文档 - 修正原SQL的语法缺陷:添加两个子查询的日期关联条件避免笛卡尔积,用全连接+ISNULL处理单日无收入/无支出的场景,空值补0避免显示异常,增加日期排序保证输出顺序符合预期
- 所有数据库连接、数据流、PDF文档对象都用
using语句包裹,离开作用域后自动释放资源,无需手动判断连接状态、手动释放对象 - 将文档关闭、弹窗提示逻辑移到资源块内,确保所有数据写入完成后再关闭文档,不会出现内容截断问题
内容的提问来源于stack exchange,提问作者Lochana Liyange
相关产品推荐
相关产品推荐

