You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows应用中NPOI实现多记录Excel(XLS)转SQL的循环取值问题

解决NPOI读取多记录Excel并批量插入数据库的问题

首先,我注意到你的现有代码存在两个核心问题:一是硬编码了行和列索引,只能处理单条记录;二是直接拼接SQL字符串,存在严重的SQL注入风险,而且遇到空值或特殊字符(比如单引号)时极易出错。下面给你一步步的解决方案:

1. 明确Excel结构逻辑

从你的代码推测,Excel的结构应该是这样的:

  • 左侧行对应字段名(比如第1行是CaseNo,第2行是Customer,以此类推)
  • 每一条CaseInfo记录对应右侧的一列,列之间用空列分隔(比如第1列是第一条记录,第3列是第二条,第5列是第三条...)

我们需要遍历所有包含有效数据的列,为每一列构建一条插入数据。

2. 重构代码:循环处理多条记录 + 参数化SQL

下面是修改后的完整代码,我标注了关键改进点:

using NLog;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using System;
using System.Data.SqlClient;
using System.IO;
using System.Windows.Forms;

namespace Excel_to_SQL
{
    public partial class Excel_to_SQL : Form
    {
        public Excel_to_SQL()
        {
            InitializeComponent();
        }

        private readonly static NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();

        private void Excel_to_SQL_Load(object sender, EventArgs e)
        {
            HSSFWorkbook workbook;
            using (FileStream file = new FileStream(@"c:\CaseInfo.xls", FileMode.Open, FileAccess.Read))
            {
                workbook = new HSSFWorkbook(file);
            }

            ISheet sheet = workbook.GetSheet("CaseInfo");
            if (sheet == null)
            {
                Logger.Error("Sheet 'CaseInfo' not found in the Excel file.");
                Application.Exit();
                return;
            }

            // 开始遍历所有有效记录列(初始从第1列开始,步长2适配空列分隔)
            int currentColumn = 1;
            while (true)
            {
                // 检查当前列是否有有效数据(取第一个字段的单元格判断)
                var firstCell = sheet.GetRow(1)?.GetCell(currentColumn);
                if (firstCell == null || string.IsNullOrWhiteSpace(GetCellValue(firstCell)))
                {
                    break; // 无有效数据,退出循环
                }

                // 构建参数化SQL语句(避免注入风险)
                string insertSql = @"INSERT INTO [test].[dbo].[CaseInfo] 
                                    (CaseNo,Customer,DisassembleType,ProjectNo,DispatchedVendor,
                                    AuthStoreNo,AuthStoreName,AuthStoreArea,AuthStoreContact,AuthStoreContactCell,
                                    BusinessTime,BusinessAddress,ContractAddress,PDQType,DualModuleMode,
                                    ECRConn,ConnType,LOGO,ElectronicInvoice,Broadband,
                                    AddInstructions,RequireNo,ContractNo,DTID,ProjectName,
                                    DispatchedDept,OldAuthStoreNo,Header) 
                                    VALUES 
                                    (@CaseNo,@Customer,@DisassembleType,@ProjectNo,@DispatchedVendor,
                                    @AuthStoreNo,@AuthStoreName,@AuthStoreArea,@AuthStoreContact,@AuthStoreContactCell,
                                    @BusinessTime,@BusinessAddress,@ContractAddress,@PDQType,@DualModuleMode,
                                    @ECRConn,@ConnType,@LOGO,@ElectronicInvoice,@Broadband,
                                    @AddInstructions,@RequireNo,@ContractNo,@DTID,@ProjectName,
                                    @DispatchedDept,@OldAuthStoreNo,@Header)";

                using (SqlConnection sqlConn = new SqlConnection(@"Data Source=testsvr;Initial Catalog=test;Trusted_Connection=true"))
                {
                    sqlConn.Open();
                    using (SqlTransaction transaction = sqlConn.BeginTransaction("CaseInfoTransaction"))
                    {
                        try
                        {
                            using (SqlCommand cmd = new SqlCommand(insertSql, sqlConn, transaction))
                            {
                                // 第一组参数:从当前列取值
                                cmd.Parameters.AddWithValue("@CaseNo", GetCellValue(sheet.GetRow(1).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@Customer", GetCellValue(sheet.GetRow(2).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@DisassembleType", GetCellValue(sheet.GetRow(3).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@ProjectNo", GetCellValue(sheet.GetRow(4).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@DispatchedVendor", GetCellValue(sheet.GetRow(5).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@AuthStoreNo", GetCellValue(sheet.GetRow(6).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@AuthStoreName", GetCellValue(sheet.GetRow(7).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@AuthStoreArea", GetCellValue(sheet.GetRow(8).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@AuthStoreContact", GetCellValue(sheet.GetRow(9).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@AuthStoreContactCell", GetCellValue(sheet.GetRow(10).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@BusinessTime", GetCellValue(sheet.GetRow(11).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@BusinessAddress", GetCellValue(sheet.GetRow(12).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@ContractAddress", GetCellValue(sheet.GetRow(13).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@PDQType", GetCellValue(sheet.GetRow(14).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@DualModuleMode", GetCellValue(sheet.GetRow(15).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@ECRConn", GetCellValue(sheet.GetRow(16).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@ConnType", GetCellValue(sheet.GetRow(17).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@LOGO", GetCellValue(sheet.GetRow(18).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@ElectronicInvoice", GetCellValue(sheet.GetRow(19).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@Broadband", GetCellValue(sheet.GetRow(20).GetCell(currentColumn)));
                                cmd.Parameters.AddWithValue("@AddInstructions", GetCellValue(sheet.GetRow(21).GetCell(currentColumn)));

                                // 第二组参数:从当前列+2的位置取值(适配你的原代码逻辑)
                                int secondColumn = currentColumn + 2;
                                cmd.Parameters.AddWithValue("@RequireNo", GetCellValue(sheet.GetRow(1).GetCell(secondColumn)));
                                cmd.Parameters.AddWithValue("@ContractNo", GetCellValue(sheet.GetRow(2).GetCell(secondColumn)));
                                cmd.Parameters.AddWithValue("@DTID", GetCellValue(sheet.GetRow(3).GetCell(secondColumn)));
                                cmd.Parameters.AddWithValue("@ProjectName", GetCellValue(sheet.GetRow(4).GetCell(secondColumn)));
                                cmd.Parameters.AddWithValue("@DispatchedDept", GetCellValue(sheet.GetRow(5).GetCell(secondColumn)));
                                cmd.Parameters.AddWithValue("@OldAuthStoreNo", GetCellValue(sheet.GetRow(6).GetCell(secondColumn)));
                                cmd.Parameters.AddWithValue("@Header", GetCellValue(sheet.GetRow(7).GetCell(secondColumn)));

                                // 执行插入
                                int rowsAffected = cmd.ExecuteNonQuery();
                                Logger.Debug($"Inserted {rowsAffected} record(s) for column {currentColumn}");
                            }

                            transaction.Commit();
                        }
                        catch (Exception ex)
                        {
                            transaction.Rollback();
                            Logger.Error(ex, $"Failed to insert record for column {currentColumn}");
                            throw; // 可根据需求选择是否抛出异常,或继续处理下一条
                        }
                    }
                }

                // 跳到下一个记录列(步长2适配空列分隔)
                currentColumn += 2;
            }

            MessageBox.Show("所有记录导入完成!");
            Application.Exit();
        }

        // 辅助方法:安全获取单元格值,处理空值和不同单元格类型
        private string GetCellValue(ICell cell)
        {
            if (cell == null)
                return string.Empty;

            switch (cell.CellType)
            {
                case CellType.String:
                    return cell.StringCellValue.Trim();
                case CellType.Numeric:
                    if (DateUtil.IsCellDateFormatted(cell))
                        return cell.DateCellValue.ToString("yyyy-MM-dd HH:mm:ss");
                    else
                        return cell.NumericCellValue.ToString();
                case CellType.Boolean:
                    return cell.BooleanCellValue.ToString();
                case CellType.Formula:
                    // 处理公式计算结果
                    using (var evaluator = new HSSFFormulaEvaluator(cell.Sheet.Workbook))
                    {
                        var cellValue = evaluator.Evaluate(cell);
                        switch (cellValue.CellType)
                        {
                            case CellType.String:
                                return cellValue.StringValue.Trim();
                            case CellType.Numeric:
                                if (DateUtil.IsCellDateFormatted(cell))
                                    return cellValue.NumberValue.ToString("yyyy-MM-dd HH:mm:ss");
                                else
                                    return cellValue.NumberValue.ToString();
                            case CellType.Boolean:
                                return cellValue.BooleanValue.ToString();
                            default:
                                return string.Empty;
                        }
                    }
                default:
                    return string.Empty;
            }
        }
    }
}

3. 关键改进点说明

  • 循环处理多记录:通过遍历列索引(步长为2适配空列分隔),自动处理所有有效记录,直到无数据为止。
  • 参数化SQL:彻底消除SQL注入风险,同时自动处理特殊字符,比字符串拼接更安全可靠。
  • 通用单元格取值:GetCellValue方法兼容字符串、数字、日期、布尔值、公式等多种单元格类型,避免类型错误异常。
  • 事务与错误处理:每条记录插入都在事务中,失败时回滚并记录日志,便于排查问题。
  • 空值校验:遍历前检查列是否有有效数据,避免无效循环。

4. 适配Excel结构调整

如果你的Excel列分隔逻辑不是步长2,或者字段对应的行索引有变化,只需调整:

  • currentColumn的初始值和步长
  • 参数添加部分对应的行、列索引

内容的提问来源于stack exchange,提问作者Kliff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:32:28