当交错相互影响的语句时,SQLite的定义行为是什么?

当交错相互影响的语句时,SQLite的定义行为是什么?,sqlite,Sqlite,在SQLite中,如果我准备了一条SELECT语句并开始单步执行它,那么在到达结果的最后一行之前,我会执行另一条对我正在单步执行的SELECT语句有影响的语句,预期的结果是什么 我在SQLite文档中找不到任何关于应该发生什么的内容,但在多线程环境中编程时,这似乎是一种非常常见的情况 是一个C++文件,可以在Windows上编译和运行,以演示情况。 #include "stdafx.h" #include "sqlite3.h" #include <Windows.h> #inclu

在SQLite中,如果我准备了一条SELECT语句并开始单步执行它,那么在到达结果的最后一行之前,我会执行另一条对我正在单步执行的SELECT语句有影响的语句,预期的结果是什么

我在SQLite文档中找不到任何关于应该发生什么的内容,但在多线程环境中编程时,这似乎是一种非常常见的情况

是一个C++文件,可以在Windows上编译和运行,以演示情况。

#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;
}

SQLite在同一事务中并发语句的行为既没有记录也没有定义

如您所见,当SELECT的光标尚未到达表的该部分时,可能会看到新插入的记录。 但是,如果SQLite需要创建一个临时结果表以进行排序或分组,则该表中稍后的更改将不会出现在该结果中。 是否有临时表可能取决于查询优化器所做的决定,因此这通常是不可预测的

如果多个线程访问同一个连接,SQLite将在每个sqlite3_步骤调用周围锁定DB。 这可以防止数据损坏,但您仍然会遇到这样的问题:自动事务在其最后一个活动语句结束时结束,如果存在其他活动语句,则显式事务将无法提交


多线程程序最好每个线程至少使用一个连接。

可能的答案:感谢链接。这两个我以前都看过。第一个简单地解释了SQLite如何确保数据库永远不会损坏并在提交更改时处理错误,第二个简单地说是的,它是线程安全的,但没有解释预期的行为。这个页面似乎更有帮助,并且在最后包含了一些关于临时表的有趣点,我需要阅读更多关于这些的内容,看看它们是否提供了这个问题的解决方案:答案很好,如果作者在文档中的某个地方承认这一点就好了。如果有一个特性可以指定特定事务的结果不受其他并发事务的影响,那就更好了。特定事务的结果总是不受其他并发事务的影响。您的问题是同一事务中的并发语句。为了使SELECT结果不受这些影响,SQLite需要创建数据的临时副本(即使不需要),或者更改其文件格式以存储相同数据的旧版本和新版本。这两个都不适合Lite设计。每个线程单独连接是否也能解决记录在案的线程相互覆盖错误MSGS问题?但是,如果您正在执行线程,那么最好使用与其他线程隔离的事务,错误信息存储在连接对象中。