【问题标题】:SQLSTATE 24000 - Invalid cursor state ODBC VS c++SQLSTATE 24000 - 无效的游标状态 ODBC VS c++
【发布时间】:2016-03-09 12:08:08
【问题描述】:

我有一个相当简单的 c++ 应用程序,它使用 ODBC 连接到使用存储过程进行填充的 SQL Server 实例。

我使用 wstring 对象的行来构建一个查询,然后将其传递给存储过程。如果我运行一次,一切正常 - 但是,我希望能够遍历大量代码(3000+ excel 行),并且当我这样做时,会出现标题中提到的光标错误。

这是存储过程:

GO
CREATE PROCEDURE s_addHistorical
    @Symbol nchar(10),@Date datetime,
    @Open decimal(8,2),@Close decimal(8,2),@MinPrice decimal(8,2),
    @MaxPrice decimal(8,2),@Volume int
AS 
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    BEGIN TRANSACTION
    MERGE HistoricalStock WITH (UPDLOCK) AS myTarget
        USING (SELECT @Symbol AS Symbol,
        @Date AS Date, @Open AS [Open], @Close AS [Close],
        @MinPrice AS MinPrice, @MaxPrice AS MaxPrice,@Volume AS Volume) AS mySource
        ON mySource.Symbol = myTarget.Symbol AND mySource.Date = myTarget.Date
        WHEN MATCHED 
            THEN UPDATE 
                SET [Open] = mySource.[Open], [Close] = mySource.[Close],
                MinPrice = mySource.MinPrice, MaxPrice = mySource.MaxPrice, Volume = mySource.Volume            
        WHEN NOT MATCHED
            THEN
                INSERT(Symbol,Date,[Open],[Close],MinPrice,MaxPrice,Volume)
                VALUES(@Symbol,@Date,@Open,@Close,@MinPrice,@MaxPrice,@Volume);
    COMMIT 
GO

这是连接器:

#include "stdafx.h"
#include "database_con.h"

////////////////////////////////////////////////////////////////////////
// Show errors from the SQLHANDLE

void database_con::show_error(unsigned int handletype, const SQLHANDLE& handle)
{
    SQLWCHAR sqlstate[1024];
    SQLWCHAR message[1024];
    if (SQL_SUCCESS == SQLGetDiagRec(handletype, handle, 1, sqlstate, NULL, message, 1024, NULL))
        wcout << "Message: " << message << "\nSQLSTATE: " << sqlstate << endl;
}


////////////////////////////////////////////////////////////////////////
// Builds the stored procedure query.

std::wstring database_con::buildQuery(vector<std::wstring> input)
{
    std::cout << "Building the query" << std::endl;
    std::wstringstream builder;
    builder << L"EXEC sp_addHistorical " << "@Symbol='" << L"" << input.at(0) << "'," <<
        "@Date=" << (wstring)L"" << input.at(1) << "," <<
        "@Open=" << (wstring)L"" << input.at(2) << "," <<
        "@Close=" << (wstring)L"" << input.at(3) << "," <<
        "@MaxPrice=" << (wstring)L"" << input.at(4) << "," <<
        "@MinPrice=" << (wstring)L"" << input.at(5) << "," <<
        "@Volume=" << (wstring)L"" << input.at(6) << ";";
    return builder.str();
}

////////////////////////////////////////////////////////////////////////
// Adds a substring of the string before the delimiter to a vector<wstring> that is returned.

std::vector<wstring> database_con::parseData(wstring line, char delim) {
    size_t pos = 0;
    std::vector<std::wstring> vOut;
    while ((pos = line.find(delim)) != std::string::npos) {
        vOut.push_back(line.substr(0, pos));
        line.erase(0, pos + 2);
    }
    vOut.push_back(line.substr(0, pos));
    return vOut;
}

std::wstring database_con::StringToWString(const std::string& s)
{
    std::wstring temp(s.length(), L' ');
    std::copy(s.begin(), s.end(), temp.begin());
    return temp;
}

database_con::database_con(std::string historical){
    /*
    Set up the handlers
    */

    /* Allocate an environment handle */
    SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
    /* We want ODBC 3 support */
    SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
    /* Allocate a connection handle */
    SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);

    /* Connect to the DSN */
    SQLDriverConnectW(dbc, NULL, L"DRIVER={SQL Server};SERVER=ERA-PC-STUART\\JBK_DB;DATABASE=master;UID=geo;PWD=kalle123;", SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
    /* Check for success */
    if (SQL_SUCCESS != SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt))
    {
        show_error(SQL_HANDLE_DBC, dbc);
        std::cout << "Failed to connect";
    }
    std::wstringstream stream(StringToWString(historical));
    std::wstring line;
    while (std::getline(stream, line)) {
        vector<wstring> vHistorical = parseData(L"" + line, ',');
        std::wstring SQL = buildQuery(vHistorical);

        if (SQL_SUCCESS != SQLExecDirectW(stmt, const_cast<SQLWCHAR*>(SQL.c_str()), SQL_NTS)) {
            show_error(SQL_HANDLE_STMT, stmt);
        }

    }

}

database_con::~database_con() {

}

我一直在 google 和 SO 上四处寻找,但我似乎找不到在我当前的问题中可用的任何问题。他们中的大多数似乎都围绕着发送回某种提取的存储过程,而我的 SP 只是插入/更新。

任何形式的帮助都会非常感激。 :)

有人吗?

【问题讨论】:

    标签: c++ sql-server stored-procedures odbc


    【解决方案1】:

    您需要调用SQLCloseCursor 来释放光标。

    将您的代码更改为:

    while (std::getline(stream, line)) {
        vector<wstring> vHistorical = parseData(L"" + line, ',');
        std::wstring SQL = buildQuery(vHistorical);
    
        if (SQL_SUCCESS != SQLExecDirectW(stmt, const_cast<SQLWCHAR*>(SQL.c_str()), SQL_NTS)) {
            show_error(SQL_HANDLE_STMT, stmt);
        }
        // Close Cursor before next iteration starts:
        SQLRETURN closeCursRet = SQLCLoseCursor(stmt);
        if(!SQL_SUCCEEDED(closeCursRet))
        {
            // maybe add some handling for the case that closing failed.
        }
    }
    

    见:https://msdn.microsoft.com/en-us/library/ms709301%28v=vs.85%29.aspx

    【讨论】:

    • 在其中添加了一个 show_error,因为它不能正常工作,现在它在 executedirect 和 closecursor 中都给了我一个无效的游标状态。我也查看了您添加的 msdn 参考,但它并没有真正说明我当前的问题 - 比我已经知道的要多。 ://
    • 除了上述评论之外,我还尝试使用:SQLCloseCursor(hStmt); SQLFreeHandle(SQL_HANDLE_STMT,hStmt) 正如您指出的参考文献所述。这会删除错误消息,但仍然不会对查询做任何事情——这意味着它会执行它,但不会对我的数据库进行任何更改。
    • 如果你只运行一次(去掉while循环),它有效吗? SQLExecDirect 是否运行无误并且数据库显示您所做的更改?
    • 是的,正如我的帖子所提到的,如果我用一个查询运行一次它就可以完美地工作。但是我几乎不希望在创建数据库连接等时进行包装,然后执行一个查询并解构。特别是看到它将被同时使用并更新至少 3500*20000 行。 :p
    • 这很令人困惑。如果您使用“旧”方式关闭游标,它是否会改变任何内容(恕我直言)。作为测试,您可以尝试将SQLRETURN closeCursRet = SQLCLoseCursor(stmt);替换为SQLRETURN closeCursRet = SQLFreeStmt(stmt, SQL_CLOSE);
    猜你喜欢
    • 2011-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-19
    • 1970-01-01
    • 2023-04-02
    相关资源
    最近更新 更多