Быстрое пакетное выполнение в PostgreSQL - PullRequest
3 голосов
/ 16 октября 2011

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

CREATE TABLE test_table
(
  id serial NOT NULL,
  item integer NOT NULL,
  count integer NOT NULL,
  CONSTRAINT test_table_pkey PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE test_table OWNER TO postgres;

Я написал тестовый код, создал 1000 случайных значений и вставил в test_table двумя различными способами.Во-первых, используя QSqlQuery::exec()

int insert() {
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");

db.setHostName("127.0.0.1");
db.setDatabaseName("TestDB");
db.setUserName("postgres");
db.setPassword("1234");

if (!db.open()) {
    qDebug() << "can not open DB";
    return -1;
}

QString queryString = QString("INSERT INTO test_table (item, count)"
        " VALUES (:item, :count)");

QSqlQuery query;
query.prepare(queryString);

QDateTime start = QDateTime::currentDateTime();

for (int i = 0; i < 1000; i++) {

    query.bindValue(":item", qrand());
    query.bindValue(":count", qrand());

    if (!query.exec()) {
        qDebug() << query.lastQuery();
        qDebug() << query.lastError();
    }

} //end of for i

QDateTime end = QDateTime::currentDateTime();
int diff = start.msecsTo(end);
return diff;
}

Во-вторых, используя QSqlQuery::execBatch:

int batchInsert() {
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");

db.setHostName("127.0.0.1");
db.setDatabaseName("TestDB");
db.setUserName("postgres");
db.setPassword("1234");

if (!db.open()) {
    qDebug() << "can not open DB";
    return -1;
}

QString queryString = QString("INSERT INTO test_table (item, count)"
        " VALUES (:item, :count)");

QSqlQuery query;
query.prepare(queryString);

QVariantList itemList;
QVariantList CountList;

QDateTime start = QDateTime::currentDateTime();

for (int i = 0; i < 1000; i++) {

    itemList.append(qrand());
    CountList.append(qrand());

} //end of for i

query.addBindValue(itemList);
query.addBindValue(CountList);

if (!query.execBatch())
    qDebug() << query.lastError();

QDateTime end = QDateTime::currentDateTime();
int diff = start.msecsTo(end);
return diff;
}

Я обнаружил, что между ними нет никакой разницы:

int main() {
qDebug() << insert() << batchInsert();
return 1;}

Результат:

14270 14663 (milliseconds)

Как я могу улучшить его?

In http://doc.qt.io/qt-5/qsqlquery.html#execBatch цитируется:

Если база данных не поддерживает пакетную обработкуПри выполнении, драйвер будет имитировать его с помощью обычных вызовов exec ().

Я не уверен, что моя СУБД поддерживает пакетное выполнение или нет?Как я могу это проверить?

Ответы [ 3 ]

3 голосов
/ 16 октября 2011

Не уверен, что делает драйвер qt, но PostgreSQL может поддерживать запуск нескольких операторов в одной транзакции. Просто сделайте это вручную, а не пытайтесь использовать встроенную функцию драйвера.

Попробуйте изменить оператор SQL на

BEGIN TRANSACTION;

Для каждой итерации цикла запускайте оператор вставки.

INSERT HERE;

После завершения цикла для всех 1000 записей, выполните это На том же соединении.

COMMIT TRANSACTION;

Кроме того, 1000 строк не так много для тестирования, вы можете попробовать 100 000 или больше, чтобы убедиться, что пакет qt действительно не помог.

1 голос
/ 07 июня 2013

Выпустив 1000 операторов вставки, вы получите 1000 обращений в базу данных. Это занимает довольно много времени (задержка сети и планирования). Поэтому постарайтесь уменьшить количество операторов вставки!

Допустим, вы хотите:

insert into test_table(item, count) values (1000, 10);
insert into test_table(item, count) values (1001, 20);
insert into test_table(item, count) values (1002, 30);

Преобразуйте его в один запрос, и запросу потребуется менее половины времени:

insert into test_table(item, count) values (1000, 10), (1001, 20), (1002, 30);

В PostgreSQL есть еще один способ написать это:

insert into test_table(item, count) values (
  unnest(array[1000, 1001, 1002])
  unnest(array[10, 20, 30]));

Моя причина для представления второго способа заключается в том, что вы можете передать все содержимое большого массива в одном параметре (протестировано в C # с драйвером базы данных "Npgsql"):

insert into test_table(item, count) values (unnest(:items), unnest(:counts));
  • items - это параметр запроса со значением int[]{100, 1001, 1002}
  • counts - параметр запроса со значением int[]{10, 20, 30}

Сегодня я сократил время выполнения 10 000 вставок в C # с 80 до 550 мс с помощью этой техники. Это просто. Кроме того, с транзакциями не возникает проблем, поскольку один оператор никогда не разделяется на несколько транзакций.

Надеюсь, это работает и с драйвером Qt PostgreSQL. На стороне сервера вам нужен PostgreSQL> = 8.4., Поскольку более старые версии не предоставляют unnest (но могут быть обходные пути).

0 голосов
/ 08 февраля 2013

Вы можете использовать QSqlDriver :: hasFeature с аргументом QSqlDriver :: BatchOperations

В источниках 4.8 я обнаружил, что только oci (oracle) поддерживает BatchOperations. Не знаю, почему бы не использовать оператор COPY для postgresql в драйвере psql.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...