2016-03-09 61 views
0

我有一個相當簡單的C++應用程序,它使用ODBC連接到使用存儲過程填充的SQL Server實例。SQLSTATE 24000 - 無效的遊標狀態ODBC VS C++

我使用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() { 

} 

我在谷歌和SO一直四處尋找,但我似乎無法找到在我目前的問題中可用的任何問題。他們中的大多數似乎圍繞存儲過程發回某種提取物,而我的SP只是插入/更新。

任何形式的幫助將大大appriciated。 :)

有人嗎?

回答

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

+0

加在它show_error因爲它不能正常工作,現在給了我一個無效的遊標狀態中均executedirect並在closecursor。 我看着你添加的msdn參考資料,但它並沒有真正地說我目前的問題太多 - 比我已經知道的要多。 :/ – geostocker

+0

除了上面的評論,我試圖用這個: SQLCloseCursor(hStmt); SQLFreeHandle(SQL_HANDLE_STMT,hStmt) 作爲您提到的參考。 這將刪除錯誤消息,但仍不會對查詢執行任何操作 - 意味着它會執行該操作,但不會更改我的數據庫。 – geostocker

+0

如果你只做一次運行(刪除while循環),它是否工作? SQLExecDirect是否運行沒有錯誤,並且數據庫顯示您所做的更改? – erg

相關問題