我有很多数据,我希望在最短的时间内插入数据库。我做了一些测试。我在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)
我该如何改进?
引用http://doc.qt.io/qt-5/qsqlquery.html#execBatch:
如果数据库不支持批处理执行,则驱动程序将执行 使用传统的exec()调用来模拟它。
我不确定我的DBMS是否支持批量执行? 我该怎么测试呢?
答案 0 :(得分:3)
不确定qt驱动程序的作用,但PostgreSQL可以支持在一个事务中运行多个语句。只需手动完成,而不是尝试使用驱动程序的内置功能。</ p>
尝试将您的SQL语句更改为
BEGIN TRANSACTION;
对于循环的每次迭代运行一个insert语句。
INSERT HERE;
一旦所有1000条记录发生循环结束,就发出此问题。在你的同一个连接上。
COMMIT TRANSACTION;
另外1000行测试不多,你可能想尝试100,000或更多,以确保qt批次没有帮助。
答案 1 :(得分:1)
通过发出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}
今天,我用这种技术将C#中10,000个插入的运行时间从80s缩短到了550ms。这很简单。此外,交易没有任何麻烦,因为单个语句永远不会分成多个交易。
我希望这也适用于Qt PostgreSQL驱动程序。在服务器端,你需要PostgreSQL&gt; = 8.4。,因为旧版本不提供unnest
(但可能有解决办法)。
答案 2 :(得分:0)
您可以将QSqlDriver :: hasFeature与参数QSqlDriver :: BatchOperations一起使用
在4.8源代码中,我发现只有oci(oracle)支持BatchOperations。不知道为什么不在psql驱动程序中使用postgresql的COPY语句。