CRecordset, INSERT, MSSQL, IDENTITY



  • Ich hab ein altes Programm, welches die CDatabase/CRecordset Klassen verwendet, um mit einem SQL Server zu plaudern.
    Dummerweise brauche ich jetzt die Möglichkeit ein INSERT zu machen, und danach den generierten Wert der IDENTITY Spalte zu bekommen.

    Kann mir jmd. nen Tip geben wie man das am einfachsten macht?
    AddNew(), Update() und dann Spalte auslesen geht auf jeden Fall nicht.



  • Lol, da gabs mal einen Beitrag (siehe http://www.c-plusplus.net/forum/viewtopic-var-t-is-260367.html), da gings auch darum und du hast da auch geantwortet. Dort wurde was mit einer OUTPUT-Klausel vorgeschlagen.



  • Hehe, ja, das SELECT SCOPE_IDENTITY() ist mir auch vollkommen klar.
    Das dumme ist bloss, in der Applikation hab ich so von CRecordset abgeleitete Klassen, und da kann ich nicht einfach ein SELECT SCOPE_IDENTITY() hinten dran schreiben.

    Wenn ich SELECT SCOPE_IDENTITY() danach als einzelnes Statement raustrete funktioniert das irgendwie nicht. Zumindest nicht wenn ich wieder ein (nicht abgeleitetes) CRecordset dafür verwende.

    Und ExecuteSql von CDatabase kann leider keine Return-Werte/Cursor/... zurückgeben.

    Ich mach es jetzt derzeit so, dass ich ein INSERT in einen String formatiere, und dann mit dieser Funktion ausführe:

    bool ExecuteSqlReturnInteger(CDatabase* pDatabase, char const* sqlStatement, long& out_identity)
    {
    	out_identity = 0;
    
    	HSTMT statementHandle = 0;
    	SQLRETURN rc = ::SQLAllocStmt(pDatabase->m_hdbc, &statementHandle);
    	if (!SQL_SUCCEEDED(rc))
    	{
    		LOG_ERROR("ExecuteSqlReturnInteger: SQLAllocStmt failed!");
    		return false;
    	}
    
    	CStringA str(sqlStatement);
    	rc = ::SQLExecDirectA(statementHandle, reinterpret_cast<SQLCHAR*>(str.GetBuffer()), SQL_NTS);
    	if (!SQL_SUCCEEDED(rc))
    	{
    		::SQLFreeStmt(statementHandle, SQL_DROP);
    		LOG_ERROR("ExecuteSqlReturnInteger: SQLExecDirect failed!");
    		return false;
    	}
    
    	long identity = 0;
    	SQLINTEGER len;
    	rc = ::SQLBindCol(statementHandle, 1, SQL_C_LONG, &identity, sizeof(identity), &len);
    	if (!SQL_SUCCEEDED(rc))
    	{
    		::SQLFreeStmt(statementHandle, SQL_DROP);
    		LOG_ERROR("ExecuteSqlReturnInteger: SQLBindCol failed!");
    		return false;
    	}
    
    	rc = ::SQLFetch(statementHandle);
    	if (!SQL_SUCCEEDED(rc))
    	{
    		::SQLFreeStmt(statementHandle, SQL_DROP);
    		LOG_ERROR("ExecuteSqlReturnInteger: SQLFetch failed!");
    		return false;
    	}
    
    	out_identity = identity;
    
    	::SQLFreeStmt(statementHandle, SQL_DROP);
    	return true;
    }
    

    Allerdings fände ich es schöner, wenn ich nicht das "gekapselte" ODCB (CRecordset, CDatabase) mit "rohem" ODBC mischen müsste.



  • Es gibt noch die "russische" Methode:

    per Trigger die eingefügte ID als Exception raisen, die kannst du im catch dann auswerten.



  • waaaaaaaaaaaah
    nönö, dann lieber so wie ich's jetzt mache 🙂


Anmelden zum Antworten