2011-10-16 57 views
3

我有很多數據,我想在最短的時間內插入數據庫。我做了一些測試。我在PostgreSQL中創建一個表(使用下面的腳本):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) 

^h我能改善它嗎?

http://doc.qt.io/qt-5/qsqlquery.html#execBatch已被引用:

如果數據庫不支持批量處理,司機會 使用傳統的exec()調用模擬。

我不確定我的DBMS是否支持批處理執行? 我該如何測試它?

回答

3

不知道qt驅動程序在做什麼,但PostgreSQL可以支持在一個事務中運行多個語句。只需手動完成,而不是嘗試使用驅動程序的內置功能。

試着改變你的SQL語句

BEGIN TRANSACTION; 

For循環的每個迭代運行一個INSERT語句。

INSERT HERE; 

一旦所有1000條記錄都發生循環結束,請發出此問題。在你的同一個連接上。

COMMIT TRANSACTION; 

1000行也沒有太多的測試,你可能想嘗試100,000或更多,以確保qt批次真的沒有幫助。

+0

確保您的連接不是自動提交。如果這是我的答案的目標將會丟失,因爲它會在每次發佈插入時提交,而不是等待最終的提交語句。 – Kuberchaun

0

您可以使用QSqlDriver :: hasFeature與參數QSqlDriver :: BatchOperations

在4.8的來源,我發現只有OCI(Oracle)的支持BatchOperations。不知道爲什麼不在psql驅動程序中使用postgresql的COPY語句。

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次插入的運行時間從80秒減少到了550毫秒。這很容易。此外,交易沒有任何麻煩,因爲單一的陳述永遠不會分成多個交易。

我希望這也適用於Qt PostgreSQL驅動程序。在服務器端,您需要PostgreSQL> = 8.4,因爲舊版本不提供unnest(但可能有解決方法)。

+0

GREAT !!!!性能改進非常驚人。 謝謝。 – EngineeredBrain