Excel數據導出小記

文章目錄

  • 前言
  • 一、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;有效管理和更新環境中的動態變化。…