2013-04-24 53 views
1

在SQLite的,如果我準備SELECT聲明,並開始通過它加強,則達到了結果的最後行之前我執行的是對SELECT聲明,我踩着通過影響另一份聲明中,什麼是預期的結果?當交錯彼此影響的語句時,SQLite定義的行爲是什麼?

我找不到什麼是應該發生的SQLite的文檔中的任何東西,但它似乎是在多線程環境中編程時,一個非常常見的情況。

下面是可以編譯和運行在Windows上展示的情況一個C++文件。

#include "stdafx.h" 
#include "sqlite3.h" 
#include <Windows.h> 
#include <iostream> 
#include <Knownfolders.h> 
#include <Shlobj.h> 
#include <wchar.h> 
#include <comdef.h> 

using namespace std; 

int exec_sql(sqlite3 *db, const char* sql) 
{ 
    char *errmsg; 
    int result = sqlite3_exec(db, sql, NULL, NULL, &errmsg); 
    if (result != SQLITE_OK) { 
     cout << errmsg << endl; 
     return -1; 
    } 

    return 0; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    cout << "Running jsqltst with SQLite version: "; 
    cout << sqlite3_libversion(); 
    cout << endl; 

    PWSTR userhome; 

    if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Profile, NULL, NULL, &userhome))) { 
     cout << "Failed getting user home dir\n"; 
     return -1; 
    } 

    wcout << "User home: " << userhome << endl; 

    wchar_t *ws1 = userhome, *ws2 = L"\\test.sqlite"; 
    wstring dbpath_str(ws1); 
    dbpath_str += wstring(ws2); 
    _bstr_t dbpath(dbpath_str.c_str()); 

    cout << "DB path: " << dbpath << endl; 

    sqlite3 *db; 

    int result = sqlite3_open_v2(dbpath, &db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL); 
    if (result != SQLITE_OK) { 
     cout << sqlite3_errmsg(db) << endl; 
     return -1; 
    } 

    const char * create_stmt = "CREATE TABLE IF NOT EXISTS atable (id INTEGER PRIMARY KEY, name TEXT, number INTEGER);"; 
    if (exec_sql(db, create_stmt) != 0) { 
     return -1; 
    } 

    const char * delete_stmt = "DELETE FROM atable;"; 
    if (exec_sql(db, delete_stmt) != 0) { 
     return -1; 
    } 

    const char * insert_stmt = "INSERT INTO atable (name,number) VALUES ('Beta',77),('Alpha',99);"; 
    if (exec_sql(db, insert_stmt) != 0) { 
     return -1; 
    } 

    sqlite3_stmt* select_ss; 
    const char * select_stmt = "SELECT * FROM atable;"; 
    result = sqlite3_prepare_v2(db, select_stmt, -1, &select_ss, NULL); 
    if (result != SQLITE_OK) { 
     cout << sqlite3_errmsg(db) << endl; 
     return -1; 
    } 

    int i = 0; 
    boolean gotrow; 
    do { 
     result = sqlite3_step(select_ss); 
     gotrow = result == SQLITE_ROW; 
     if (gotrow) { 
      i++; 
      cout << "I got a row!" << endl; 

      if (i == 1) { 
       if (exec_sql(db, insert_stmt) != 0) { 
        return -1; 
       } 
      } 
     } 
    } while (gotrow); 

    cout << "Last result: " << result << ", errstr: " << sqlite3_errstr(result) << endl; 

    result = sqlite3_finalize(select_ss); 
    if (result != SQLITE_OK) { 
     cout << sqlite3_errmsg(db) << endl; 
     return -1; 
    } 

    return 0; 
} 
+0

可能的答案:http://www.sqlite.org/transactional.html http://www.sqlite.org/atomiccommit.html http://www.sqlite.org/faq.html#q6 – Patashu 2013-04-24 01:24:07

+0

感謝鏈接。我以前看過這兩個。第一個簡單地解釋了SQLite如何確保數據庫永不損壞,並在提交更改時處理錯誤,第二個簡單地說「它是線程安全的」,但沒有解釋預期的行爲。 – satur9nine 2013-04-24 01:26:37

+0

這個頁面似乎更有幫助,它包含了關於臨時表的一些有趣點,我將需要閱讀更多關於這些問題的內容,看看它們是否提供瞭解決此問題的方法:http://www.sqlite。 org/cvstrac/wiki?p = MultiThreading – satur9nine 2013-04-24 18:52:07

回答

3

SQLite在同一事務中的併發語句的行爲既沒有記錄也沒有定義。

正如你所看到的,當SELECT的光標還沒有達到該表的那部分新插入的記錄可能可以看到。 但是,如果SQLite需要創建用於排序或分組的臨時結果表,則表中以後的更改不會顯示在該結果中。 您是否擁有臨時表可能取決於查詢優化器所做的決定,因此這通常是不可預測的。

如果多個線程同時訪問同一個連接時,SQLite將鎖定DB圍繞每一個sqlite3_step通話。 這可以防止數據損壞,但是當自動事務的最後一個活動語句結束時,您仍然會遇到自動事務結束的問題,並且如果有其他活動語句,則顯式事務將失敗COMMIT

多線程程序最好每個線程使用(至少)一個連接。

+0

好的答案,如果作者在文檔的某處承認了這一點,那將會很好。如果有一個特性指定特定事務的結果不受其他併發事務的影響,那將會更好。 – satur9nine 2013-04-24 18:44:38

+0

特定事務的結果總是*不受其他併發事務影響。你的問題是* same *事務中的併發語句。爲了使'SELECT'結果不受這些影響,SQLite需要創建數據的臨時副本,即使不需要,或者更改其文件格式以存儲相同數據的舊版本和新版本。這些都不符合「精簡版」的設計。 – 2013-04-25 09:02:15

+0

每個線程單獨的連接也解決了記錄線程覆蓋每個其他errmsgs問題?但是,如果你使用線程,最好使用與其他線程隔離的事務, – 2014-12-03 21:21:36

相關問題