【问题标题】:Bulk C# datatable to postgresql table批量 C# 数据表到 postgresql 表
【发布时间】:2016-07-29 07:23:17
【问题描述】:

我有一个包含数千条记录的数据表。 我有一个与数据表相同的字段的 postgres 表。 我希望每天截断这个表并再次填充数据表的数据。我见过 sql bulk copy,但它在 postgres 上不可用。 那么,哪种方法最有效呢?

  • 每条记录一次插入
  • 多次插入:插入表值(1,1),(1,2),(1,3),(2,1);
  • 从数据表中选择并使用 linq 插入到 postgres 中?不知道...

谢谢。

【问题讨论】:

标签: c# postgresql datatable bulkinsert sqlbulkcopy


【解决方案1】:

PostgreSQL 确实有一个大容量副本(它实际上叫做copy),它有一个很好的.NET 包装器。如果你正在加载,你想使用NpgsqlCopyIn,如果你正在提取数据你可以使用NpgsqlCopyOut.

您的问题在细节上有点含糊——我不知道您的数据表中的字段或您的实际数据库的任何内容,因此以这个作为简要示例,了解如何使用 C#/PostgreSQL 将数据批量插入表中:

    NpgsqlCopyIn copy = new NpgsqlCopyIn("copy table1 from STDIN WITH NULL AS '' CSV;",
        conn);
    copy.Start();

    NpgsqlCopySerializer cs = new NpgsqlCopySerializer(conn);
    cs.Delimiter = ",";

    foreach (var record in RecordList)
    {
        cs.AddString(record.UserId);
        cs.AddInt32(record.Age);
        cs.AddDateTime(record.HireDate);
        cs.EndRow();
    }

    cs.Close();
    copy.End();

-- 2019 年 8 月 27 日编辑--

Npgsql 的结构已经完全改变。下面是上面相同示例的样板,使用二进制导入(文本也可用):

using (var writer = conn.BeginBinaryImport(
    "copy user_data.part_list from STDIN (FORMAT BINARY)"))
{
    foreach (var record in RecordList)
    {
        writer.StartRow();
        writer.Write(record.UserId);
        writer.Write(record.Age, NpgsqlTypes.NpgsqlDbType.Integer);
        writer.Write(record.HireDate, NpgsqlTypes.NpgsqlDbType.Date);
    }

    writer.Complete();
}

【讨论】:

【解决方案2】:

也许您可以查看我的另一个答案,其中我描述了我为这个问题创建的一个小助手,使用另一个助手真的很容易: https://stackoverflow.com/a/46063313/6654362

编辑: 我最近遇到了类似的问题,但我们使用的是 Postgresql。我想使用有效的bulkinsert,结果非常困难。我还没有在这个数据库上找到任何合适的免费库。我只找到了这个助手: https://bytefish.de/blog/postgresql_bulk_insert/ 这也在 Nuget 上。我编写了一个小型映射器,它可以像实体框架那样自动映射属性:

public static PostgreSQLCopyHelper<T> CreateHelper<T>(string schemaName, string tableName)
        {
            var helper = new PostgreSQLCopyHelper<T>(schemaName, "\"" + tableName + "\"");
            var properties = typeof(T).GetProperties();
            foreach(var prop in properties)
            {
                var type = prop.PropertyType;
                if (Attribute.IsDefined(prop, typeof(KeyAttribute)))
                    continue;
                switch (type)
                {
                    case Type intType when intType == typeof(int) || intType == typeof(int?):
                        {
                            helper = helper.MapInteger("\"" + prop.Name + "\"",  x => (int?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type stringType when stringType == typeof(string):
                        {
                            helper = helper.MapText("\"" + prop.Name + "\"", x => (string)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type dateType when dateType == typeof(DateTime) || dateType == typeof(DateTime?):
                        {
                            helper = helper.MapTimeStamp("\"" + prop.Name + "\"", x => (DateTime?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type decimalType when decimalType == typeof(decimal) || decimalType == typeof(decimal?):
                        {
                            helper = helper.MapMoney("\"" + prop.Name + "\"", x => (decimal?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type doubleType when doubleType == typeof(double) || doubleType == typeof(double?):
                        {
                            helper = helper.MapDouble("\"" + prop.Name + "\"", x => (double?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type floatType when floatType == typeof(float) || floatType == typeof(float?):
                        {
                            helper = helper.MapReal("\"" + prop.Name + "\"", x => (float?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type guidType when guidType == typeof(Guid):
                        {
                            helper = helper.MapUUID("\"" + prop.Name + "\"", x => (Guid)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                }
            }
            return helper;
        }

我按以下方式使用它(我有一个名为 Undertaking 的实体):

var undertakingHelper = BulkMapper.CreateHelper<Model.Undertaking>("dbo", nameof(Model.Undertaking));
undertakingHelper.SaveAll(transaction.UnderlyingTransaction.Connection as Npgsql.NpgsqlConnection, undertakingsToAdd));

我展示了一个带有事务的示例,但它也可以通过从上下文中检索到的正常连接来完成。 takingsToAdd 是可枚举的普通实体记录,我想将它们批量插入到数据库中。

经过几个小时的研究和尝试,我得到了这个解决方案,正如您所期望的那样,它的速度要快得多,而且最终易于使用且免费!我真的建议你使用这个解决方案,不仅因为上面提到的原因,而且因为它是唯一一个我对 Postgresql 本身没有问题的解决方案,许多其他解决方案都可以完美地工作,例如 SqlServer。

【讨论】:

    【解决方案3】:

    有一些选项可以批量插入 PostgreSQL。

    例如,在我的图书馆中,我使用的是SQL Copy

    COPY TableName (Column1, Column2, Column3) FROM STDIN BINARY
    

    免责声明:我是项目的所有者Bulk-Operations.NET

    这个库可以很容易地执行任何类型的批量操作:

    • 批量插入
    • 批量更新
    • 批量删除
    • 批量合并

    在包括 PostgreSQL 在内的多个数据库提供程序中

    // Easy to use
    var bulk = new BulkOperation(connection);
    bulk.BulkInsert(dt);
    bulk.BulkUpdate(dt);
    bulk.BulkDelete(dt);
    bulk.BulkMerge(dt);
    

    【讨论】:

      【解决方案4】:

      正如在其他答案中所说,没有内置解决方案,只有一些帮助库(免费和非免费),我个人提出了自己的解决方案。这样做的好处是

      • 免费,易于使用
      • 不需要额外的映射设置,它会重用来自 DB 本身和 EF DbContext 的元数据
      • 使用动态代码构建来提高性能

      用法是这样的:

      var uploader = new NpgsqlBulkUploader(context);
      var data = GetALotOfData();
      uploader.Insert(data);
      // OR
      uploader.Update(data);
      

      我描述了there

      【讨论】:

        【解决方案5】:

        上述解决方案要求您指定列数及其类型,从而使您的代码表具体化。如果您的表相对较小并且具有相同数量的列和相同/兼容的列类型,则可以以通用方式进行。假设您要将 Sqlite 表迁移到 PosgreSql:

        // Get data from SqlLite database table
        SQLiteConnection sqliteConnection = new SQLiteConnection(new SQLiteConnectionStringBuilder() { DataSource = @"C:\dataBase.sqlite" }.ConnectionString);
        sqliteConnection.Open();
        var reader = new SQLiteCommand($"SELECT * from table_which_we_want_to_migrate").ExecuteReader();
        var dataFromSqliteTable = new DataTable() { CaseSensitive = true };
        dataFromSqliteTable.Load(reader);
        
        // Connect to PostgreSql database
        var connection = new NpgsqlConnection(new NpgsqlConnectionStringBuilder()
        {
            Host = "localhost",
            Port = 5432,
            Database = "DatabaseName",
            Username = "UserName",
            Password = "Password"
        }.ToString());
        connection.Open();
        
        // Insert every row from the Sqlite table into PostgreSql table
        foreach (DataRow row in dataFromSqliteTable.Rows)
        {
            // Create an NpgsqlParameter for every field in the column
            var parameters = new List<DbParameter>();
            for (var i = 0; i < dataFromSqliteTable.Columns.Count; i++)
            {
                parameters.Add(new NpgsqlParameter($"@p{i}", row[i]));
            }
            var parameterNames = string.Join(", ", parameters.Select(p => p.ParameterName));
            
            // Create an INSERT SQL query which inserts the data from the current row into PostgreSql table
            var command = new NpgsqlCommand(
                $"INSERT INTO table_which_we_want_to_migrate VALUES ({parameterNames})",
                connection);
            command.Parameters.AddRange(parameters.ToArray());
            command.ExecuteNonQuery();
        }
        

        另一种方法是使用命令行实用程序并通过 CSV 文件导入/导出。这种方式速度更快,甚至适用于大表:

        sqlite3 dataBase.sqlite ".output 'temporaryFile.csv.tmp'" ".headers off" ".mode csv" "SELECT * FROM table_which_we_want_to_migrate;" ".quit"
        psql --command="\copy table_which_we_want_to_migrate FROM 'temporaryFile.csv.tmp' DELIMITER ',' CSV"
        

        【讨论】:

          猜你喜欢
          • 2021-05-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-14
          • 2016-03-08
          • 2018-07-22
          • 2019-12-02
          • 1970-01-01
          相关资源
          最近更新 更多