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