将DataTable bulk的整个值插入postgreSQL表

在SQL中,我们执行类似这样的操作来批量插入数据表

SqlBulkCopy copy = new SqlBulkCopy(sqlCon); copy.DestinationTableName = strDestinationTable; copy.WriteToServer(dtFrom); Blockquote 

但是在PostgreSQL中如何做这个操作

使用参数简单插入

您的项目需要引用以下程序集: Npgsql 。 如果此引用在Visual Studio中不可见,则:

  1. 浏览到连接器的安装文件夹
  2. 执行: GACInstall.exe
  3. 重新启动Visual Studio

样本表

 CREATE TABLE "OrderHistory" ( "OrderId" bigint NOT NULL, "TotalAmount" bigint, CONSTRAINT "OrderIdPk" PRIMARY KEY ("OrderId") ) WITH ( OIDS=FALSE ); ALTER TABLE "OrderHistory" OWNER TO postgres; GRANT ALL ON TABLE "OrderHistory" TO postgres; GRANT ALL ON TABLE "OrderHistory" TO public; ALTER TABLE "OrderHistory" ALTER COLUMN "OrderId" SET (n_distinct=1); GRANT SELECT("OrderId"), UPDATE("OrderId"), INSERT("OrderId"), REFERENCES("OrderId") ON "OrderHistory" TO public; GRANT SELECT("TotalAmount"), UPDATE("TotalAmount"), INSERT("TotalAmount"), REFERENCES("TotalAmount") ON "OrderHistory" TO public; 

示例代码

请务必使用以下指令:

 using Npgsql; using NpgsqlTypes; 

在您的方法中输入以下源代码:

 // Make sure that the user has the INSERT privilege for the OrderHistory table. NpgsqlConnection connection = new NpgsqlConnection("PORT=5432;TIMEOUT=15;POOLING=True;MINPOOLSIZE=1;MAXPOOLSIZE=20;COMMANDTIMEOUT=20;COMPATIBLE=2.2.4.3;DATABASE=test;HOST=127.0.0.1;PASSWORD=test;USER ID=test"); connection.Open(); DataSet dataSet = new DataSet(); NpgsqlDataAdapter dataAdapter = new NpgsqlDataAdapter("select * from OrderHistory where OrderId=-1", connection); dataAdapter.InsertCommand = new NpgsqlCommand("insert into OrderHistory(OrderId, TotalAmount) " + " values (:a, :b)", connection); dataAdapter.InsertCommand.Parameters.Add(new NpgsqlParameter("a", NpgsqlDbType.Bigint)); dataAdapter.InsertCommand.Parameters.Add(new NpgsqlParameter("b", NpgsqlDbType.Bigint)); dataAdapter.InsertCommand.Parameters[0].Direction = ParameterDirection.Input; dataAdapter.InsertCommand.Parameters[1].Direction = ParameterDirection.Input; dataAdapter.InsertCommand.Parameters[0].SourceColumn = "OrderId"; dataAdapter.InsertCommand.Parameters[1].SourceColumn = "TotalAmount"; dataAdapter.Fill(dataSet); DataTable newOrders = dataSet.Tables[0]; DataRow newOrder = newOrders.NewRow(); newOrder["OrderId"] = 20; newOrder["TotalAmount"] = 20.0; newOrders.Rows.Add(newOrder); DataSet ds2 = dataSet.GetChanges(); dataAdapter.Update(ds2); dataSet.Merge(ds2); dataSet.AcceptChanges(); connection.Close(); 

对绩效的思考

原始post未提及性能要求。 要求解决方案必须:

  1. 使用DataTable插入
  2. 不使用循环插入数据

如果您要插入大量数据,那么我建议您查看一下您的性能选项。 Postgres文档建议您:

  • 禁用自动提交
  • 使用COPY命令
  • 删除索引
  • 删除外键约束
  • 等等

有关优化Postgres插入的更多信息,请查看:

  • PostgresSql.org:插入数据
  • PostgresSql.org:插入+性能提示
  • StackOverflow:如何加快PostgreSQL中的插入性能

此外,还有许多其他因素会影响系统的性能。 有关高级别的介绍,请查看:

  • ADO.NET SQL Server性能瓶颈
    • 该post概述了用于优化性能的一般(即非SqlServer)策略。

其他选择

  • .NET连接器是否支持Postgres Copy命令?
    • 如果没有,您可以下载Npgsql连接器的源代码并添加您自己的BulkCopy()方法。 请务必先查看源代码的许可协议。
  • 检查Postgres是否支持表值参数
    • 这种方法允许您将表传入Postgres函数,然后可以将数据直接插入目标。
  • 从供应商处购买Postgres .NET连接器,其中包括所需的function。

其他参考文献

  • Postgres .NET Connector – 免费开源

我前段时间遇到同样的问题。 到目前为止,似乎还没有“随时可用”的解决方案。

我已经阅读过这篇文章,并在那时构建了一个类似的解决方案,直到今天仍在使用。 它基于文本查询,从STDIN读取文件。 它使用ADO.NET Postgre数据提供程序Npgsql 。 您可以根据DataTable创建一个大字符串(或临时文件,内存使用原因),并使用COPY命令将其用作文本查询。 在我们的例子中,它比inser教学行快得多。

也许这不是一个完整的解决方案,但可能是一个很好的开始和我所知道的任何事情。 🙂

我还发现,还没有“随时可用”的解决方案。 可能你可以查看我的另一个答案,其中我描述了我为这个问题创建的一个小帮手,使用另一个帮助器非常简单: https : //stackoverflow.com/a/46063313/6654362我认为这是目前最好的解决方案。 如果post死了,我从链接发布了解决方案。

编辑:我最近遇到了类似的问题,但我们使用的是Postgresql。 我想使用有效的bulkinsert,结果很难。 我没有在这个DB上找到任何适当的免费库。 我只找到了这个帮助器: https ://bytefish.de/blog/postgresql_bulk_insert/,它也在Nuget上。 我编写了一个小的映射器,它以entity framework的方式自动映射属性:

 public static PostgreSQLCopyHelper CreateHelper(string schemaName, string tableName) { var helper = new PostgreSQLCopyHelper("dbo", "\"" + tableName + "\""); var properties = typeof(T).GetProperties(); foreach(var prop in properties) { var type = prop.PropertyType; if (Attribute.IsDefined(prop, typeof(KeyAttribute)) || Attribute.IsDefined(prop, typeof(ForeignKeyAttribute))) 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("dbo", nameof(Model.Undertaking)); undertakingHelper.SaveAll(transaction.UnderlyingTransaction.Connection as Npgsql.NpgsqlConnection, undertakingsToAdd)); 

我展示了一个带事务的示例,但也可以通过从上下文中检索的普通连接来完成。 undertakingsToAdd是可枚举的普通实体记录,我想将其插入到DB中。

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

你真的想使用DataTable吗? 如果您可以使用EF Code First,那么您可以使用本文中的代码。 本文的想法是使用Npgsql COPY作为基础+从DB本身获取元数据,将其与模型类中的元数据合并,并动态生成用于写入数据的代码。

用法是这样的:

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