文章目录

  • 前言
  • 一、DataTable =>EXCEL
  • 二、DBReader =>Excel (NPOI)
  • 三、分页查询 DbReader=>Excel (MiniExcel)
  • 总结:

前言

最近经历了一次数据量比较大的导出,也做了各种优化尝试,这里稍记录一下

一、DataTable =>EXCEL

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using DataTable = System.Data.DataTable;
using Microsoft.Office.Interop.Excel;namespace ClassLibrary
{public class ExcelHelper{/// <summary>/// dt输出到新的Excel/// </summary>public static void OutPut(DataTable dt,string Path, string Filename){// 初始化 Excel 应用程序Application excelApp = new Application();Workbook workbook = null;Worksheet worksheet = null;try{// 设置可见性和默认路径excelApp.Visible = false; // 隐藏应用程序窗口if (!Directory.Exists(Path)) // 验证文件夹是否存在{Directory.CreateDirectory(Path); // 若不存在,则创建新文件夹}// 添加一个新的工作簿workbook = excelApp.Workbooks.Add();// 获取第一个工作表worksheet = (Worksheet)workbook.Sheets[1];//列名for (int i = 0; i < dt.Columns.Count; i++){worksheet.Cells[1, i + 1] = dt.Columns[i].ColumnName;}int rows = 2;//当前行// 获取 DataTable 列数和行数int rowCount = dt.Rows.Count;int colCount = dt.Columns.Count;// 构建数据部分的二维数组if (dt.Rows.Count > 0){object[,] dataValues = new object[rowCount, colCount];for (int r = 0; r < rowCount; r++){for (int c = 0; c < colCount; c++){dataValues[r, c] = dt.Rows[r][c];}}// 定义目标范围(从 (2, 2) 开始)Range startCell = (Range)worksheet.Cells[2, 1]; // 起始单元格Range endCell = (Range)worksheet.Cells[2 + rowCount - 1, 1 + colCount - 1]; // 结束单元格Range targetRange = worksheet.get_Range(startCell, endCell);// 写入数据targetRange.Value = dataValues;// 自动调整列宽worksheet.Columns.AutoFit();}//宽度自适应workbook.SaveAs(Filename);workbook.Close(false);}catch (Exception ex){return;}finally{if (worksheet != null) Marshal.ReleaseComObject(worksheet);if (workbook != null) Marshal.ReleaseComObject(workbook);if (excelApp != null){excelApp.Quit();Marshal.ReleaseComObject(excelApp);}GC.Collect();GC.WaitForPendingFinalizers();}}}
}

.netframework4.8,支持的Microsoft.Office.Interop.Excel。
将DataTable转换为二维数组,划定范围,一次性塞进去。
比逐单元格赋值效率更高

二、DBReader =>Excel (NPOI)

using NPOI.SS.UserModel;
using NPOI.XSSF.Streaming;
using System.Data;
using System.Data.Common;
public class ExcelHelper
{public static async Task<bool> OutPut(DbDataReader reader, string filePath){try{const int maxRowsPerSheet = 1_000_000;  // 每个Sheet最大行数const int bufferSize = 1000;            // 行缓存大小(优化内存)// 初始化流式工作簿(关键内存优化)using var workbook = new SXSSFWorkbook(null, bufferSize, true);int sheetIndex = 1;ISheet currentSheet = workbook.CreateSheet($"Sheet_{sheetIndex}");// 创建标题行IRow headerRow = currentSheet.CreateRow(0);for (int i = 0; i < reader.FieldCount; i++){headerRow.CreateCell(i).SetCellValue(reader.GetName(i));}int rowIndex = 1;  // 数据行从1开始(0是标题行)while (reader.Read()){// 达到最大行数时切换Sheetif (rowIndex >= maxRowsPerSheet){// 正确调用无参数FlushRows(NPOI 2.7.3+)((SXSSFSheet)currentSheet).FlushRows();  // 刷新当前Sheet[^1]sheetIndex++;currentSheet = workbook.CreateSheet($"Sheet_{sheetIndex}");rowIndex = 1;  // 新Sheet重置行索引}// 创建数据行IRow dataRow = currentSheet.CreateRow(rowIndex);// 写入所有列数据(类型安全处理)for (int col = 0; col < reader.FieldCount; col++){var cell = dataRow.CreateCell(col);// 根据数据类型安全写入if (reader.IsDBNull(col)){cell.SetCellValue((string)null);}else{switch (Type.GetTypeCode(reader.GetFieldType(col))){case TypeCode.String:cell.SetCellValue(reader.GetString(col));break;case TypeCode.DateTime:cell.SetCellValue(reader.GetDateTime(col));break;case TypeCode.Int16:cell.SetCellValue(reader.GetInt16(col));break;case TypeCode.Int32:cell.SetCellValue(reader.GetInt32(col));break;case TypeCode.Int64:cell.SetCellValue(reader.GetInt64(col));break;// case TypeCode.Decimal:// cell.SetCellValue(reader.GetDecimal(col));// break;case TypeCode.Double:cell.SetCellValue(reader.GetDouble(col));break;case TypeCode.Single:cell.SetCellValue(reader.GetDouble(col));break;case TypeCode.Boolean:cell.SetCellValue(reader.GetBoolean(col));break;default:cell.SetCellValue(reader.GetValue(col).ToString());break;}}}rowIndex++;}// 最终写入文件(使用异步提升性能)using var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None,81920, true);// 分块写入策略var writeTask = Task.Run(() => workbook.Write(fs, false));// 进度刷新控制while (!writeTask.IsCompleted){if (fs.Position % (10 * bufferSize) == 0){await fs.FlushAsync(); // 异步刷新缓冲区}await Task.Delay(50); // 减少CPU占用}await writeTask;return true;}catch (Exception ex){return false;}}
}
using Microsoft.Data.SqlClient;
using (DbDataReader reader = await cmd.ExecuteReaderAsync())
{return  await ExcelHelper.OutPut(reader, extra);
}
//查询数据后直接塞进去就行

DBReader=>DataTable=>json=>datable =>excel 通过API 传输过于占用资源,
优化:
DBReader=>excel ,直接输出到服务器共享文件夹。
流输出效率更改、省略传输转换、异步内存清除、百万条分Sheet兼容2007。

三、分页查询 DbReader=>Excel (MiniExcel)

using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using MiniExcelLibs;
namespace ExcelExport
{public class ExcelHelper{public void GetData(){SqlConnection sqlCon;try{var sheets = new Dictionary<string, object>();string excelFilePath = @"D:\Excel\DataFile.xlsx";string connectionString = ConnectionString;SqlCommand sqlcmd;sqlCon = new SqlConnection(connectionString);sqlCon.Open();sqlcmd = new SqlCommand(sql, sqlCon);//先取页数 =符合条件总条数/每页条数 maxLoopCounter = Convert.ToInt32(sqlcmd.ExecuteScalar());for (int i = 1; i <= maxLoopCounter; i++) //循环查询每页数据{string sheetName = "Sheet" + i.ToString();//  string sheetName = "Sheet5" ;sqlCon = new SqlConnection(connectionString);sqlCon.Open();string sql = "";sqlcmd = new SqlCommand(sql, sqlCon);sheets.Add(sheetName, sqlcmd.ExecuteReader());//按页写入sheet}MiniExcel.SaveAs(excelFilePath, sheets);}catch (Exception exception){}}}
}
SELECT *
FROM BigDataTable
ORDER BY CreateDate DESC
OFFSET @PageIndex * @PageSize ROWS
FETCH NEXT @PageSize ROWS ONLY; 

可惜的是FETCH 只支持SQL2012+,低版本就只有转存实体表的方式:

declare @ntile_value int =1000000 //一百万一页
SELECT id, NTILE(@ntile_value) OVER (ORDER BY id) AS page  INTO temp_BigTable FROM BigTable;
--使用 NTILE 添加页码字段->写入新表
--个人觉得建表、清表、表占用判断,也挺麻烦

总结:

服务器流导出更快,使用NPOI、和MiniExcel等都是异曲同工。数据量百万级,我觉得应用层分页更好,数据更大那数据库分页后传输更好。Interop作为微软官方com组件确实更方便,但跨平台就不行了。总的来说NPOI确实各方面更适用一些。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/85149.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/85149.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/85149.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入理解链表数据结构:从Java LinkedList到自定义实现

引言 链表作为基础数据结构之一&#xff0c;在Java集合框架中以LinkedList的形式提供。本文将深入分析Java原生LinkedList的实现机制&#xff0c;并介绍我自定义实现的MyLinkedList&#xff0c;最后对比两者的设计差异与实现特点。 Java原生LinkedList解析 基本结构 Java的…

【深度学习】卷积神经网络(CNN):计算机视觉的革命性引擎

卷积神经网络&#xff08;CNN&#xff09;&#xff1a;计算机视觉的革命性引擎 一、算法背景&#xff1a;视觉智能的进化之路1.1 传统视觉处理的困境1.2 神经科学的启示 二、算法理论&#xff1a;CNN的核心架构2.1 基础组成单元卷积层&#xff1a;特征提取引擎池化层&#xff1…

使用@SpringJUnitConfig注解开发遇到的空指针问题

Spring测试中的版本陷阱&#xff1a;SpringJUnitConfig与JUnit版本兼容性深度解析 一个看似简单的空指针异常&#xff0c;背后可能隐藏着JUnit版本不匹配的“幽灵”。 一、SpringJUnitConfig&#xff1a;Spring与JUnit 5的桥梁 SpringJUnitConfig是Spring TestContext框架为**…

[2025CVPR]AdcSR:一种高效实世界图像超分辨率的对抗扩散压缩方法

目录 1. 背景与挑战 2. AdcSR模型概述 2.1 模型架构 2.2 训练策略 3. 公式与原理 4. 创新点 5. 实验与结果 5.1 实验设置 5.2 结果对比 5.3 消融实验 6. 结论 在计算机视觉领域&#xff0c;图像超分辨率&#xff08;Image Super-Resolution, ISR&#xff09;一直是一…

Go 语言中的字符串基本操作

这篇文章已经放到腾讯智能工作台的知识库啦&#xff0c;链接在这里&#xff1a;ima.copilot-Go 入门到入土。要是你有啥不懂的地方&#xff0c;就去知识库找 AI 聊一聊吧。 本篇将详细讲解 Go 语言中与字符串相关的操作。 1、rune 和 字符串长度 1、Go 函数语法约定 在开始…

数学建模会议笔记

看似优化模型 建立整数规划模型 用优化软件、启发式方法、精确方法求解 建立图论和组合优化模型用组合优化方法、启发式方法求解 建立博弈论模型 数据统计分析与可视化- 数据拟合、参数估计、插值、数据的标准化、去伪补全相关度分析、分类、聚类等 最优化理论和方法 线性规划…

学习昇腾开发的六天--ACL应用开发之运行第一个实例

1、下载一个实例&#xff0c;运行一个图像分类实例&#xff08;环境&#xff1a;Ubuntu22.04&#xff0c;硬件&#xff1a;昇腾310B1&#xff0c;加速模块&#xff1a;atlas 200i a2&#xff09; samples: CANN Samples - Gitee.com 目录结构如下&#xff1a; ├── data │…

可灵AI-快手公司自主研发的一款AI视频与图像生成工具

可灵AI是由快手公司自主研发的一款AI视频与图像生成工具&#xff0c;于2024年6月正式推出。以下是对其的详细介绍&#xff1a; 核心功能 AI视频生成&#xff1a; 文生视频&#xff1a;输入文字描述&#xff0c;AI可自动生成匹配的视频片段。图生视频&#xff1a;上传图片&…

创客匠人解析:存量时代创始人 IP 打造与免费流量池策略

在存量竞争的商业环境中&#xff0c;企业如何突破增长瓶颈&#xff1f;创客匠人结合新潮传媒创始人张继学的实战洞察&#xff0c;揭示 “品牌 IP” 双轮驱动下的免费流量池构建逻辑&#xff0c;为知识变现与创始人 IP 打造提供新思路。 一、存量时代的流量革命&#xff1a;从…

提升语义搜索效率:LangChain 与 Milvus 的混合搜索实战

我从不幻想人生能够毫无波折&#xff0c;但我期望遭遇困境之际&#xff0c;自身能够成为它的克星。 概述 LangChain与Milvus的结合构建了一套高效的语义搜索系统。LangChain负责处理多模态数据&#xff08;如文本、PDF等&#xff09;的嵌入生成与任务编排&#xff0c;Milvus作…

MySQL配置简单优化与读写测试

测试方法 先使用sysbench对默认配置的MySQL单节点进行压测&#xff0c;单表数据量为100万&#xff0c;数据库总数据量为2000万&#xff0c;每次压测300秒。 sysbench --db-drivermysql --time300 --threads10 --report-interval1 \--mysql-host192.168.0.10 --mysql-port3306…

猎板深耕透明 PCB,解锁电子设计新边界

在电子技术快速迭代的当下&#xff0c;猎板始终关注行业前沿&#xff0c;透明 PCB 作为极具创新性的技术&#xff0c;正在改变电子设备的设计与应用格局。​ 从传统的绿色、棕色 PCB 到如今的透明 PCB&#xff0c;其突破在于特殊基材与导电材料的运用&#xff0c;实现 85%-92%…

FLAML:快速轻量级自动机器学习框架

概述 FLAML&#xff08;Fast and Lightweight AutoML&#xff09;是微软开发的一个高效的自动机器学习&#xff08;AutoML&#xff09;框架。它专注于在有限的计算资源和时间约束下&#xff0c;自动化机器学习管道的构建过程&#xff0c;包括特征工程、模型选择、超参数调优等…

Github 以及 Docker的 wsl --list --online无法访问问题

修改电脑DNS 腾讯 DNS IP&#xff1a;119.29.29.29 备用&#xff1a;182.254.116.116 阿里DNS IP&#xff1a;223.5.5.5 223.6.6.6 百度DNS IP:180.76.76.76 谷歌DNS IP:8.8.8.8

Go 语言中的变量和常量

这篇文章已经放到腾讯智能工作台的知识库啦&#xff0c;链接在这里&#xff1a;ima.copilot-Go 入门到入土。要是你有啥不懂的地方&#xff0c;就去知识库找 AI 聊一聊吧。 1、变量的声明与使用 我们来探讨编程语言中最核心的概念之一&#xff1a;变量。 1、静态语言中的变量…

破局传统订货!云徙渠道订货系统赋能企业数字化渠道升级

在数字化浪潮的推动下&#xff0c;传统经销商订货模式面临着诸多挑战&#xff0c;如信息孤岛、系统崩溃、移动化不足等问题。云徙渠道订货系统凭借其创新的数字化架构和强大的功能模块&#xff0c;正在成为企业实现渠道数字化转型的重要工具。 系统功能与创新 云徙渠道订货系统…

SQL关键字三分钟入门:UNION 与 UNION ALL —— 数据合并全攻略

在处理数据时&#xff0c;有时我们需要将来自不同表或同一表的不同查询结果合并在一起。例如&#xff1a; 合并两个部门的员工名单&#xff1b;将多个地区的销售数据汇总&#xff1b;显示某段时间内所有新增和修改的记录。 这时候&#xff0c;我们就需要用到 SQL 中非常强大的…

SNMPv3 的安全命名空间详解

1. 安全命名空间的本质 安全命名空间是 SNMPv3 的核心安全机制&#xff0c;通过 上下文&#xff08;Context&#xff09; 实现&#xff1a; #mermaid-svg-6cV9146nTFF1zCMJ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#merma…

【嵌入式硬件实例】-555定时器实现烟雾和易燃气体泄露检测

555定时器实现烟雾和易燃气体泄露检测 文章目录 555定时器实现烟雾和易燃气体泄露检测1、555定时器介绍2、MQ-2 气体/烟雾传感器模块介绍3、硬件准备与接线在本文中,我们将使用555定时器和MQ-2气体传感器构建一个气体泄漏检测和报警系统。它在煤气泄漏期间用作家庭安全警报器。…

【机器人】DualMap 具身导航 | 动态场景 开放词汇语义建图 导航系统

DualMap 是一个在线的开放词汇语义映射系统&#xff0c;使得机器人能够通过自然语言查询在动态变化的环境中理解和导航 双地图导航&#xff0c;结合全局抽象地图进行高层次候选选择&#xff0c;以及局部具体地图进行精确目标定位&#xff0c;有效管理和更新环境中的动态变化。…