Подтвердить что ты не робот

Вставьте всю ценность массива DataTable в таблицу postgreSQL.

В SQL мы делаем что-то вроде этого для объемной вставки в datatable

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

Blockquote

но в PostgreSQL, как это сделать

4b9b3361

Ответ 1

Простая вставка с использованием параметров

Вашему проекту необходимо будет ссылаться на следующую сборку: Npgsql. Если эта ссылка не видна в Visual Studio, то:

  • найдите папку установки соединителя
  • Выполнить: GACInstall.exe
  • Перезапустите 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();

Мысли о производительности

В оригинальной публикации не упоминалось о требованиях к производительности. Было предложено, чтобы решение должно:

  • с помощью DataTable
  • вставить данные без использования цикла

Если вы вставляете значительные объемы данных, я бы предложил вам взглянуть на ваши параметры производительности. Документация Postgres предполагает, что вы:

  • Отключить автокоманду
  • Используйте команду COPY
  • Удалить индексы
  • Удалить ограничения внешнего ключа
  • и др.

Для получения дополнительной информации об оптимизации вложений Postgres, пожалуйста, взгляните на:

Кроме того, существует множество других факторов, которые могут повлиять на производительность системы. Для ознакомления с высоким уровнем ознакомьтесь с:

Другие параметры

  • Поддерживает ли .NET-коннектор команду Postgres COPY?
    • Если нет, вы можете загрузить исходный код для коннектора Npgsql и добавить свой собственный BulkCopy(). Обязательно сначала просмотрите лицензионное соглашение с исходным кодом.
  • Проверьте, поддерживает ли Postgres параметр значения таблицы.
    • Этот подход позволяет вам передать таблицу в функцию Postgres, которая затем может вставить данные непосредственно в пункт назначения.
  • Приобретайте соединитель Postgres.NET от поставщика, который включает требуемую функцию.

Дополнительные ссылки

Ответ 2

У меня такая же проблема. Кажется, до сих пор нет "готового к использованию" решения.

Я прочитал этот пост и построил аналогичное решение в то время, которое до сегодняшнего дня находится в эффективном использовании. Он основан на текстовых запросах, которые читают файлы из STDIN. Он использует ADO.NET Postgre Data Provider Npgsql. Вы можете создать большую строку (или временный файл, причину использования памяти) на основе вашего DataTable и использовать его как текстовый запрос с помощью команды COPY. В нашем случае это было намного быстрее, чем научить строки.

Возможно, это не полное решение, но может быть хорошим началом и всем, что я знаю об этом.:)

Ответ 3

Я также обнаружил, что пока нет готового к использованию решения. Вероятно, вы можете проверить мой другой ответ, в котором я описываю небольшой помощник, который я создал для этой проблемы, используя простой помощник: fooobar.com/questions/15074/... Я считаю, что в настоящее время это лучшее решение. Я отправил решение из ссылки в случае, если сообщение умерло.

Изменить: Я недавно столкнулся с подобной проблемой, но мы использовали Postgresql. Я хотел использовать эффективный bulkinsert, что оказалось довольно сложным. Я не нашел в этой БД надлежащей бесплатной библиотеки. Я нашел только этого помощника: https://bytefish.de/blog/postgresql_bulk_insert/ который также находится на Nuget. Я написал небольшой картограф, который автоматически сопоставил свойства как Entity Framework:

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

Я показал пример с транзакцией, но это также можно сделать с обычным подключением, полученным из контекста. предпринимателиToAdd перечислимы для обычных записей сущностей, которые я хочу загрузить в DB.

Это решение, которое я получил после нескольких часов исследований и попыток, так же, как вы могли ожидать гораздо быстрее и, наконец, легко использовать и бесплатно! Я действительно советую вам использовать это решение не только по причинам, упомянутым выше, но и потому, что это единственный, с которым у меня не было проблем с самим Postgresql, многие другие решения работают безупречно, например, с SqlServer.

Ответ 4

Вы действительно хотите использовать DataTable для этого? Если вы можете использовать EF Code First, тогда вы можете использовать код из этой статьи . Идея статьи состоит в том, чтобы использовать Npgsql COPY в качестве базы + извлечения метаданных из самого БД, объединить ее с метаданными из классов моделей и динамически генерировать код для записи данных.

Использование выглядит так:

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