MsSql, Daten in C++ einlesen



  • Hallo Community,

    Habe ein kleines Problem beim Auslesen von MsSql-Datenbanken.
    Ich verwende Visual C++ Studio 2008 Pro, mit dem SQL Server 2008, welche ich über OLEDB 'verbinde'.

    Die Kommunikation zwischen beiden funktiert tadellos. Das einzige Problemchen was sich für mich jetzt noch stellt ist folgendes:

    Ich habe eine kleine Testtabelle entworfen welches mir zu einem Namen Zahlenwerte (hier Alter) speichert. Diese sind im SQL wie folgt deklariert

    Alter = int
    Age = smallint
    Old = bigint
    Older = tinyint
    Oldest = nchar(10)

    Wenn ich mit einem einfachen SELECT-Befehl der mir die ganze Tabelle ausgeben sollte erhalte in zurück in C++ folgendes Ergebnis:

    Zeile	Nummer	Name	Alter	Age	Old	Older	Oldest	
    0        1         Hugo    18       1       18              18       	
    1        2         Peter   36       3       36              36       	
    2        3         Heinz   48       4       48              48
    

    Wie ihr seht bleibt die Spalte die in SQL mit 'tinyint' deklariert ist leer. Und die Spalte die mit 'smallint' deklariert ist wird nur das erste Zeichen ausgelesen.

    Meine Frage hierzu ist nun kann ich über C++ generell keine 'tinyint' und
    'smallint' Werte abrufen ?? Oder muss ich beim stellen der SQL irgendwie anders vorgehen ??

    SELECT TOP 10 [Nummer] ,[Name] ,[Alter] ,[Age] ,[Old] ,[Older] ,[Oldest] 
      FROM [Oliver].[dbo].[table_02]
    

    Lautet der Query den ich über C++ in MsSql reinschicke.

    Gruß an alle lesenden...

    Ollow



  • Du musst in deiner OLEDB-Consumer-Klasse auch die entsprechenden C++-Datentypen hinterlegen. Was hast du denn da eingestellt? Normalerweise macht das der Assistent automatisch.
    Zu den Datentpyen:
    http://msdn.microsoft.com/de-de/library/ms187745.aspx



  • Hallo und erstmal danke für die Antwort, bin leider erst jetzt zu einer Rückantwort gekommen.

    Bin eigentlich auch davon ausgegangen das dies automatischen in den OLEDB Klassen die ich in das Project einbinde (oledb.h - oledberr.h - Include/sqlncli.h) automatisch geschieht. Bedeutet dies ich muss mir eine eigene Nutzer Klasse schreiben. Steh jetzt gerade ein wenig auf dem Schlauch.. !?

    Oder bedeutet dies ich muss mir einen eignen Accessor schreiben ??

    Habe meinen Quellcode mal gekürzt und am Stück zusammengefasst (ohne versch. Funktionen) wie ich beim SELECT-Befehl vorgehe:

    // Select - Query an die Datenbank senden - einfache Testfunktion
    bool nmpMsSql::QuerySendSelect(const wxString& theQuery)
    {
    // ....
    // pIDBCreateCommand und pIRowset sind im Klassenheader deklariert
    
    HRESULT hr;
    // Session erstellen
    if (FAILED(hr = nmpInitializeData.pIDBInitialize->QueryInterface(IID_IDBCreateSession, (void**) &pIDBCreateSession)))
    {
        return false;
    }
    
    // Command anfrage erstellen
    IDBCreateCommand* pIDBCreateCommand;
    if (FAILED( hr = pIDBCreateSession->CreateSession(0, IID_IDBCreateCommand, (IUnknown**) &pIDBCreateCommand)))
    {
        return false;
    }
    
    // Text aus command erzeugen
    ICommandText* pICommandText;
    if (FAILED(hr = pIDBCreateCommand->CreateCommand(0, IID_ICommandText, (IUnknown**) &pICommandText)))
    {
        return false;
    }
    
    // command wird nicht mehr gebraucht
    pIDBCreateCommand->Release();
    
    // text in richtigs format
    if (FAILED(hr = pICommandText->SetCommandText(DBGUID_DBSQL, theQuery.c_str())))
    {
        return false;
    }
    
    // Gegeben Befehl ausführen
    if (FAILED(hr = pICommandText->Execute(0, IID_IRowset, 0, 0, (IUnknown**) &pIRowset)))
    {
        return false;
    }
    
    // Text wird nicht mehr gebraucht
    pICommandText->Release();
    
    // Auswerten der Zeilen
    IColumnsInfo* pColumnsInfo;
    if (FAILED(hr = pIRowset->QueryInterface(IID_IColumnsInfo, (void**) &pColumnsInfo)))
    {
        return false;
    } 
    
    // Spalteninfos
    unsigned long ColsAnzahl = 0;
    WCHAR* pStringBuffer;
    DBCOLUMNINFO* pDBColumnInfo;
    if (FAILED( hr = pColumnsInfo->GetColumnInfo(&ColsAnzahl, &pDBColumnInfo, &pStringBuffer)))
    {
        return false;
    }
    
    // Speicher anfordern
    DBBINDING *pBindings = new DBBINDING[ColsAnzahl];
    ULONG cbColOffset = 0;
    for(ULONG j = 0; j<ColsAnzahl; j++)
    {
        pBindings[j].iOrdinal       = j+1;            
        pBindings[j].obValue        = cbColOffset;
        pBindings[j].obLength       = 0;
        pBindings[j].obStatus       = 0;
        pBindings[j].pTypeInfo      = 0;
        pBindings[j].pObject        = 0;
        pBindings[j].pBindExt       = 0;
        pBindings[j].dwPart         = DBPART_VALUE;
        pBindings[j].dwMemOwner     = DBMEMOWNER_CLIENTOWNED;
        pBindings[j].eParamIO       = DBPARAMIO_NOTPARAM;    
        pBindings[j].cbMaxLen       = pDBColumnInfo[j].ulColumnSize;
        pBindings[j].dwFlags        = 0;
        pBindings[j].wType          = DBTYPE_STR;
        pBindings[j].bPrecision     = pDBColumnInfo[j].bPrecision;
        pBindings[j].bScale         = pDBColumnInfo[j].bScale;
        cbColOffset = cbColOffset + pDBColumnInfo[j].ulColumnSize;
    }
    
    // Accessor
    IAccessor* pIAccessor;
    if (FAILED( hr = pIRowset->QueryInterface(IID_IAccessor, (void**) &pIAccessor)))
    {
        return false;
    }
    
    // Accessor laden
    HACCESSOR hAccessor;
    if (FAILED (hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, ColsAnzahl, pBindings, 0, &hAccessor, 0)))
    {
        return false;
    }
    
    // So lange eine Zeile/Reihe auslesen, bis Ende erreicht
    bool endOfDatasReached = false;
    HROW hRows[1];
    HROW* pRows = &hRows[0];
    ULONG NumRows;
    do
    {
        char* puffer = new char[cbColOffset];
        if (FAILED(hr = pIRowset->GetNextRows(0,0, 1, &NumRows, &pRows)) )
        {
            return false;
        }
        if (hr == S_OK)
        {
            if (NumRows > 0)
            {                
                for (ULONG j=0; j<NumRows; j++)
                {
                    std::fill(puffer, puffer+cbColOffset, 0);
                    hr = pIRowset->GetData(hRows[j], hAccessor, puffer);
                    if (hr==S_OK)
                    {
                        unsigned int sp = 0;
                        // Vector für eine Zeile erstellen
                        dataInlay datas;
                        // Wiederhole durchgang solange spalten vorhanden
                        while (sp != ColsAnzahl)
                        {
                            // erstelle für jede Spalte ein pair(spaltenname, wertDerSpalte);
                            twoValuesInlay twoValues;
                            // Spaltennamen auslesen
                            twoValues.first = pDBColumnInfo[sp].pwszName, true;
                            // Wert der Spalte auslesen und in einen nmpDataSet wandeln
                            twoValues.second= nmpDataSet(wxString::FromAscii(&puffer[pBindings[sp].obValue]), true);
                            // Wert in Zeilen-Vector einfügen
                            datas.push_back(twoValues);
                            sp++;
                        }
                        // Wert in Vector für das Gesamtergebnis einfügen
                        selectResult.push_back(datas);
                    }
                    else
                    {
                        // Fehler
                    }
                    pIRowset->ReleaseRows(NumRows, hRows, 0, 0, 0);
                }
            }
        }
        else
        {
            // Fängt ab DB_S_ENDOFROWSET 
            //            DB_S_ROWLIMITEXCEEDED 
            //            DB_S_STOPLIMITREACHED 
            endOfDatasReached = true;
        }
        delete puffer;
    }while(!endOfDatasReached);
    
    // ....
    }
    


  • Ollow_AM schrieb:

    Bin eigentlich auch davon ausgegangen das dies automatischen in den OLEDB Klassen die ich in das Project einbinde (oledb.h - oledberr.h - Include/sqlncli.h) automatisch geschieht. Bedeutet dies ich muss mir eine eigene Nutzer Klasse schreiben. Steh jetzt gerade ein wenig auf dem Schlauch.. !?

    Oder bedeutet dies ich muss mir einen eignen Accessor schreiben ??

    Also oledb.h und oledberr.h sind doch meines Wissens Header aus dem SDK. Woher sollen die wissen, wie deine Datenquelle die Daten bereitstellt?
    Ich habe immer über den Assistenten mir einen neue OLEDB-Consumerklasse erstellt. Dort muss man die Datenquelle angeben und es wird automatisch ein Konnektor erstellt. Mach das mal testweise und dann siehst du auch, was dir vorgeschlagen wird.



  • Jap stimmt, das sind Header aus dem SDK.
    Aber der Standard-Accessor holt mir ja so alle Daten richtig ab, abgesehen von 'tinyint' und 'smallint'. (Hmm aber vermute jetzt mal das er es vermutlich für Felder wie date oder ähnliches a net richtig machen wird. Das wär auch mal zum testen). Dafür benötigt er ja auch keine spezielle "Verknüpfung"...!?

    Allerdings habe ich mir das mal mit dem Assistenten angesehen, und das erste was ich sehe ist, daß dieser einen eigenen Accessor bastelt, in dem er die DB-Felder mit den entsprechenden C++ Typen "verknüpft".

    Was mich jetzt allerdings vor ein weiteres Problem stellt, aber zumindest habe ich jetzt mal einen Ansatzpunkt wo ich weiter arbeiten kann.

    Besten Dank und später einen schönen start ins Wochende...

    Mfg



  • Also für alle interssierten.

    Auch über den Standard-Accessor ist es möglich Typen wie 'tinyint' und 'smallint' oder 'date' etc. auszulesen.

    Mein Fehler lag beim zuweisen der DBBINDING-Eigenschaften.

    Habe es anhand dieses Beispieles von der MSDN-Seite überarbeitet:

    http://msdn.microsoft.com/en-us/library/ms721163(v=VS.85).aspx

    Jetzt funzt es so wie es brauche...

    Mfg


Anmelden zum Antworten