【问题标题】:Explicitly lock and unlock a table using ODBC使用 ODBC 显式锁定和解锁表
【发布时间】:2017-10-23 09:51:26
【问题描述】:

我必须对存储在 MSSQL Server 数据库中的数据执行一些计算,然后将结果保存在同一个数据库中。

我需要将表(部分)加载到 C++ 数据结构中,执行计算(这可能需要大量时间),最后将一些行添加到同一个表中。

问题是多个用户可以同时访问数据库,我希望表被锁定,因为数据加载到内存中,直到计算结果写入表。

使用 ODBC SDK,是否可以显式锁定和解锁表的一部分?

我试过下面的测试程序,可惜INSERT语句在StmtHandle1被释放之前就成功了:

SQLDriverConnect(ConHandle1,  NULL, (SQLCHAR *)"DRIVER={ODBC Driver 13 for SQL Server};"
                                               "SERVER=MyServer;"
                                               "DATABASE=MyDatabase;"/*, ... */);

SQLSetStmtAttr(StmtHandle1,SQL_ATTR_CONCURRENCY,(SQLPOINTER)SQL_CONCUR_LOCK,SQL_IS_INTEGER);

SQLExecDirect(StmtHandle1, (SQLCHAR *)"SELECT * FROM [MyTable] WITH (TABLOCKX, HOLDLOCK)", SQL_NTS);


SQLDriverConnect(ConHandle2, NULL, (SQLCHAR *)"DRIVER={ODBC Driver 13 for SQL Server};"
                                              "SERVER=MyServer;"
                                              "DATABASE=MyDatabase;"/*, ... */);

SQLSetStmtAttr(StmtHandle2,SQL_ATTR_CONCURRENCY,(SQLPOINTER)SQL_CONCUR_LOCK,SQL_IS_INTEGER);

SQLExecDirect(StmtHandle2, (SQLCHAR *)"INSERT INTO [MyTable] VALUES (...)", SQL_NTS);

【问题讨论】:

    标签: c++ sql sql-server odbc


    【解决方案1】:

    不幸的是 INSERT 语句在 StmtHandle1 之前成功 释放

    默认情况下,SQL Server 在自动提交模式下运行,即打开一个 tarnsaction 并为您提交。

    您请求了TABLOCKX,并且该表在您的事务期间被锁定,但您想要的是明确地打开一个事务并且不要commit/rollback 它直到您完成您的计算,即你应该使用

    begin tran; SELECT top 1 * FROM [MyTable] WITH (TABLOCKX, HOLDLOCK);
    

    而且你不需要阅读整个表格,top 1 * 就足够了。

    【讨论】:

    • 它确实锁定了表,但是当我尝试通过提交事务来解锁表时(即SQLExecDirect(StmtHandle1, (SQLCHAR *)"COMMIT TRANSACTION", SQL_NTS);)我收到错误“无效游标状态”
    • "invalid cursor state" 不是服务器错误,它与 C++ 有关。如果您无法从该会话中执行任何操作,只需将其杀死,所有锁都将被释放
    • 好的。它工作正常。我必须将当前连接的自动提交模式设置为关闭:SQLSetConnectAttr(ConHandle, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, NULL);,然后使用SQLEndTran(SQL_HANDLE_DBC, ConHandle, SQL_COMMIT);提交事务