多数据高性能 同步 到本地数据库表里

开发环境

VS2022

net4.5

上位机程序

在 C# .NET Framework 4.5 中,将多个数据同步到本地 SQL Server 数据库表,并实现高性能的 SqlCommand 插入操作,最佳实践是使用 SqlBulkCopy。这是微软提供的专门用于高效批量插入数据的类。

以下是几种不同的实现方式,从最推荐的 SqlBulkCopy 开始:

方法一:使用 SqlBulkCopy (推荐 - 最高性能)

这种方法是处理大量数据插入时最高效的方式。

cs 复制代码
using System;
using System.Data;
using System.Data.SqlClient; // 注意:.NET Framework 4.5 使用 System.Data.SqlClient
using System.Diagnostics;

public class DataSyncService
{
    private readonly string _connectionString;

    public DataSyncService(string connectionString)
    {
        _connectionString = connectionString;
    }

    /// <summary>
    /// 使用 SqlBulkCopy 将数据列表高效地插入到数据库中
    /// </summary>
    /// <typeparam name="T">数据对象的类型</typeparam>
    /// <param name="dataList">要插入的数据列表</param>
    /// <param name="tableName">目标数据库表名</param>
    public void BulkInsert<T>(IList<T> dataList, string tableName) where T : class
    {
        if (dataList == null || !dataList.Any()) return;

        // 1. 将 List<T> 转换为 DataTable
        var dataTable = ConvertToDataTable(dataList);

        // 2. 执行批量插入
        using (var connection = new SqlConnection(_connectionString))
        {
            connection.Open();
            using (var bulkCopy = new SqlBulkCopy(connection))
            {
                bulkCopy.DestinationTableName = tableName;
                
                // 设置超时时间(可选,默认30秒)
                bulkCopy.BulkCopyTimeout = 300; // 5分钟
                
                // 设置批处理大小(可选,默认值为0,表示一次性全部提交)
                // 设置一个合理的批次大小可以减少内存占用和网络压力
                bulkCopy.BatchSize = 10000;

                // 映射列(如果DataTable的列名与数据库表的列名不完全一致,需要手动映射)
                // 例如: bulkCopy.ColumnMappings.Add("Name", "DbNameColumn");
                // 如果列名完全一致,可以省略这一步。

                bulkCopy.WriteToServer(dataTable);
            }
        }
    }

    /// <summary>
    /// 将任意类型的 List 转换为 DataTable
    /// </summary>
    private DataTable ConvertToDataTable<T>(IList<T> data) where T : class
    {
        var properties = TypeDescriptor.GetProperties(typeof(T));
        var table = new DataTable();

        // 添加列
        foreach (PropertyDescriptor prop in properties)
        {
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
        }

        // 添加行
        foreach (var item in data)
        {
            var row = table.NewRow();
            foreach (PropertyDescriptor prop in properties)
            {
                row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
            }
            table.Rows.Add(row);
        }

        return table;
    }
}

// --- 使用示例 ---
class Program
{
    static void Main(string[] args)
    {
        var connectionString = "Server=localhost;Database=TestDB;Trusted_Connection=true;";
        var syncService = new DataSyncService(connectionString);

        // 假设你的数据模型
        var users = new List<User>();
        for (int i = 0; i < 100000; i++)
        {
            users.Add(new User { Id = i, Name = $"User{i}", Email = $"user{i}@example.com" });
        }

        Console.WriteLine($"开始同步 {users.Count} 条记录...");
        var sw = Stopwatch.StartNew();

        // 执行批量插入
        syncService.BulkInsert(users, "Users");

        sw.Stop();
        Console.WriteLine($"同步完成,耗时: {sw.ElapsedMilliseconds} ms");
    }
}

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

方法二:使用事务中的参数化 SqlCommand (适用于中等数量数据或复杂逻辑)

如果数据量不是特别大(例如几千到几万条),或者需要在插入过程中执行复杂的业务逻辑判断,可以使用带事务的 SqlCommand

cs 复制代码
public class DataSyncServiceWithTransaction
{
    private readonly string _connectionString;

    public DataSyncServiceWithTransaction(string connectionString)
    {
        _connectionString = connectionString;
    }

    /// <summary>
    /// 在一个事务中批量执行参数化命令进行插入
    /// </summary>
    /// <param name="dataList">要插入的数据列表</param>
    /// <param name="insertCommandText">INSERT SQL语句</param>
    public void InsertInTransaction(List<User> dataList, string insertCommandText)
    {
        using (var connection = new SqlConnection(_connectionString))
        {
            connection.Open();
            using (var transaction = connection.BeginTransaction())
            {
                try
                {
                    using (var command = new SqlCommand(insertCommandText, connection, transaction))
                    {
                        command.Parameters.Add("@Id", SqlDbType.Int);
                        command.Parameters.Add("@Name", SqlDbType.NVarChar, 255);
                        command.Parameters.Add("@Email", SqlDbType.NVarChar, 255);

                        foreach (var user in dataList)
                        {
                            command.Parameters["@Id"].Value = user.Id;
                            command.Parameters["@Name"].Value = user.Name;
                            command.Parameters["@Email"].Value = user.Email;

                            command.ExecuteNonQuery();
                        }
                    }
                    transaction.Commit();
                    Console.WriteLine("所有数据已成功插入!");
                }
                catch
                {
                    transaction.Rollback();
                    Console.WriteLine("发生错误,事务已回滚。");
                    throw;
                }
            }
        }
    }
}

对比

方法 适用场景 性能 复杂度
SqlBulkCopy 大量数据 (>10k+ 行) 最高 低 (需要转换为 DataTable)
事务中的 SqlCommand 中等数据量或需要逐条处理逻辑 较高
相关推荐
一个天蝎座 白勺 程序猿1 小时前
Apache IoTDB(18):IoTDB时序数据库的数据同步之Pipe机制与插件同步指南
数据库·apache·时序数据库·iotdb
颜颜yan_1 小时前
告别“子查询性能陷阱“:金仓数据库智能下推优化器的设计与实测
数据库·oracle
sdanss1 小时前
MySQL——表的约束
数据库·mysql
吾诺1 小时前
MySQL远程连接错误解决:“Host is not allowed to connect to this MySQL server”详解
数据库·mysql
耶叶1 小时前
Android开发:用户注册和登录的数据库代码详细解释
android·数据库·kotlin
xuansec2 小时前
【PortSwigger】SQL注入第一题:WHERE 子句注入漏洞利用 —— 检索隐藏数据
数据库·sql
艾莉丝努力练剑2 小时前
【MYSQL】MYSQL学习的一大重点:MYSQL数据类型
android·linux·数据库·人工智能·学习·mysql·网络安全
惶了个恐2 小时前
Linux系统编程第十弹——sqlite3
数据库
c++之路2 小时前
Ubuntu 22.04 完整安装与配置指南(VMware + 系统优化 + 开发环境)
linux·数据库·ubuntu