将DataTable bulk的整个值插入postgreSQL表

时间:2015-05-19 08:28:19

标签: c# asp.net-mvc postgresql datatable

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

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

Blockquote

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

4 个答案:

答案 0 :(得分:7)

使用参数进行简单插入

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

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

    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();
    

    对绩效的思考

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

    1. 使用DataTable
    2. 插入
    3. 不使用循环插入数据
    4. 如果您要插入大量数据,那么我建议您查看一下您的性能选项。 Postgres 文档建议您:

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

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

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

      其他选项

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

      其他参考资料

答案 1 :(得分:3)

我前一段时间遇到过同样的问题。似乎没有准备好使用"解决方案,直到现在。

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

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

答案 2 :(得分:2)

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

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

public static PostgreSQLCopyHelper<T> CreateHelper<T>(string schemaName, string tableName)
        {
            var helper = new PostgreSQLCopyHelper<T>("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<Model.Undertaking>("dbo", nameof(Model.Undertaking));
undertakingHelper.SaveAll(transaction.UnderlyingTransaction.Connection as Npgsql.NpgsqlConnection, undertakingsToAdd));

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

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

答案 3 :(得分:-1)

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

用法是这样的:

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