Abfrage an verknüpfte Tabellen in Access-Datenbank (ODBC)
-
Hallo Leute,
ich hab ein Problem. Ich muß für die Arbeit eine Datenbank(Access) an ein
bestehendes Programm anbinden und dafür ein Interface in VC++ programmieren.
Ich komm soweit auch ganz gut zurecht, wenn ich es nur mit einer Tabelle in der
Datenbank zu tun habe. Das mach ich dann mit einem Recordset und das klappt
auch soweit wunderbar.
Jetzt ist das Problem, das in der Datenbank, die ich anbinden soll acht
Tabellen rumoxidiern. Ich bin erstmal so vorgegangen, dass ich für jede Tabelle
ein Recordset angelegt hab. Die Tabellen sind aber untereinander verknüpft (in
Access) und meine Frage wäre jetzt, was passiert, wenn ich in der Datenbank in
einer Tabelle(1) suche, sind dann die dazu passenden Datensätze aus den
Untertabellen(2,3,4) auch in ihren dazugehörigen Recordsets gespeichert?Oder muß ich die Suchanfrage praktisch an alle dazugehörigen Tabellen stellen?
Das wäre nicht so gut.
Wie würdet ihr das machen?
(Ich möchte schon gern bei der Struktur mit den Recordsets bleiben, weil ich
sie auch zum schreiben in die DB benutzen muß.)Vielen Dank für Eure Antworten
Hermie
-
Es ist die umständliche Lösung.
Wie du genau vorgehen kannst, hängt dann wieder davon ab, was du genau machen willst. (...wie immer...
)
-
Hi estartu!
Das ging ja superfix mit der Antwort. Danke!
Also ich will folgendes machen:In der Datenbank sind wie gesagt mehrere Tabellen. Vier Tabellen enthalten
Daten, die ich für Berechnungen brauche. Die anderen Tabellen sollen dann
Ergebnisse dieser Berechnungen aufnehmen. Alle Tabellen sind untereinander
verknüpft. Zum Beispiel enthält eine Tabelle Daten zu Stoffen und diese Tabelle
ist wieder mit anderen Tabellen verknüpft in denen dann steht, welche
Basiseinheit für die Berechnung mit diesem Stoff herangezogen werden soll und
mit welchem Faktor oder aus welcher Quelle(Studie) die Daten stammen.
Die Tabellen, die für die Aufnahme der Ergebnisse zuständig sind, sind wiederum
auh mit der Tabelle für die Stoffdaten verknüpft(über einen Primärschlüssel,
der jeden Stoff eindeutig identifiziert). Und die Tabellen für die Ergebnisse
sind wiederum aufgeteilt in Tabellen, die Daten für Szenarien enthalten(mehrere
einzelne Ergebnisse, die zusammengefaßt wurden) und einer Tabelle, die die
Einzelergebnisse aufnimmt und mit der Szenarientabelle verknüpft ist.
Es hört sich glaub ich sehr verwirrend anIch hab mir das aber nicht ausgedacht
Ich hoffe mal ich konnte das jetzt rüberbringen, was mein Problem ist.
Ich hoffe auf gute AntwortenHermie
-
das geht mit SQL anweisungen.... die Tabellen sind mit Ids verknuepft und durch entsprechende SQL-Queries bekommst du dann auch die Ergebnisse für den Recordset.... ich denke es geht nur so.
zu den 8 Recordsets... das koennte man auch dynamisch gestalten... habe mir sagen lassen die MFC haelt fuer sowas auch etwas vor...
-
Hi pinorrix!
Hättest Du dafür ein Beispiel oder einen Link? Ich hab mich bevor ich das Projekt aufgehalst bekommen hab noch nie mit Datenbankprogrammierung beschäftigt.(Macht mir aber trotzdem Spaß
)
Inwieweit kann man sowas dynamisch machen und womit?
Vielen Dank für Deine Antwort!Hermie
-
Noch eine Möglichkeit:
Filter setzen (das entspricht einer WHERE-Klausel von einem SQL-Statement). Du hast also z.B. aus Tabelle einen Stoff mit der Nummer 0815. Dann kannst du einen Filter auf die Eigenschaftstabelle setzen:
CRecordset Stofftabelle;
...Open() usw. ...
CRecordset Eigenschaftstabelle;
Eigenschaftstabelle.m_strFilter = "STOFFNUMMER = " + Stofftabelle.m_STOFFNUMMER;
Eigenschaftstabelle.Open(); // oder Eigenschaftstabelle.Requery() wenn bereits offen
Sowas ist aber nicht gerade für die gute Performance bekannt. Ist eher relativ langsam.Und noch eine Möglichkeit:
erstell eine (mehrere?) Abfrage/View über alle Tabellen (mit den benötigten Datenfeldern), die bereits die Eigenschaften usw. aus den Folgetabellen zu einem Stoff aus der Führungstabelle zusammensammelt. Dann ein Recordset auf die View und ggf. noch wie oben einen Filter. Dann hast du alle benötigten und zusammengehörigen Daten im Recordset von der View und lässt die Datenbank die Arbeit machen. Ist schneller als die Möglichkeit da oben.
-
mit ner abfrage in access ist das auch getan - stimmt... bevorzugen wuerde ich SQL denn diese kommandos koennen dann im programm beliebig generiert werden und bringen den ensprechenden recordset
http://www.c-plusplus.net/forum/viewtopic.php?t=79957&highlight=
http://www.c-plusplus.net/forum/viewtopic.php?t=78758&highlight=hier steht vielleicht was dazu....
wenn du ne loesung zum dynamischen verwalten vieler beliebiger tabellen hast, bin ich interessiert....
-
Danke isabeau!
Aber was mach ich, wenn ich zu dem Stoff Nr.0815 mehrere Ergebisse bekomme?
Das ist durchaus möglich bei meinen Daten. Dann müßte ich mich ja mit MoveNext() mehrmals durch die Abfrage hangeln.
Eine View hab ich auch nicht, da ich ja nur die Klassen programmieren soll, die auf die Datenbank zugreifen. Und das soll dann wiederrum in ein bestehendes Programm eingearbeitet werden.
Ich hatte ja geschrieben, daß meine erste Datenbank soweit gut ausgelesen werden konnte. Da hatte ich aber nur eine Tabelle.
Hier ist der Code: vielleicht hilft es jemandem:[cpp] #include "Datawirk.h" class DBInterface { public: void suchenSubst(CString Such); void suchen(); CString Suchstr; void schreiben(); void lesen(); void ausgabe(); DBInterface(); virtual ~DBInterface(); long DBIID; CString DBISubstanz; CString DBICASNummer; CString DBIGruppe; CString DBIKompartiment; CString DBIEinheit; double DBIWert; CString DBIGrundlage; CString DBIAlternative; CString DBIKategorie; CString DBIStudie; double DBICahraFaktor; double DBIResourceextr; double DBILuftemission; double DBIFrischwasser; double DBISeewasser; double DBILandwboden; double DBIIndustboden; double DBILandnutz; CDatawirk DBrecObj; //Recordset auf erste Tabelle }; [/cpp]
[cpp] #include "stdafx.h" #include "Datawirk.h" //Klasse für das Recordset #include "DBInterface.h" #include <iostream.h> DBInterface::DBInterface() { DBrecObj.Open(); //Recordset öffnen DBISubstanz = DBrecObj.m_Substanz; DBIWert = DBrecObj.m_Wert; } DBInterface::~DBInterface() { DBrecObj.Close(); } void DBInterface::ausgabe() { DBrecObj.MoveFirst(); while(!(DBrecObj.IsEOF())) { DBISubstanz = DBrecObj.m_Substanz; DBIWert = DBrecObj.m_Wert; lesen(); cout<<" "<<DBISubstanz<<" "<<DBIWert<<"\n"; DBrecObj.MoveNext(); }; } void DBInterface::lesen() { DBIID = DBrecObj.m_ID; DBISubstanz = DBrecObj.m_Substanz; DBICASNummer= DBrecObj.m_CASNummer; DBIGruppe = DBrecObj.m_Gruppe; DBIKompartiment = DBrecObj.m_Kompartiment; DBIEinheit = DBrecObj.m_Einheit; DBIWert = DBrecObj.m_Wert; DBIGrundlage = DBrecObj.m_Grundlage; DBIAlternative = DBrecObj.m_Alternative; DBIKategorie = DBrecObj.m_Kategorie; DBIStudie = DBrecObj.m_Studie; DBICahraFaktor = DBrecObj.m_CahraFaktor; DBIResourceextr = DBrecObj.m_Resourceextr; DBILuftemission = DBrecObj.m_Luftemission; DBIFrischwasser = DBrecObj.m_Frischwasser; DBISeewasser = DBrecObj.m_Seewasser; DBILandwboden = DBrecObj.m_Landwboden; DBIIndustboden = DBrecObj.m_Industboden; DBILandnutz = DBrecObj.m_Landnutz; } void DBInterface::schreiben() { DBrecObj.m_ID = DBIID; DBrecObj.m_Substanz =DBISubstanz; DBrecObj.m_CASNummer = DBICASNummer; DBrecObj.m_Gruppe = DBIGruppe; DBrecObj.m_Kompartiment = DBIKompartiment; DBrecObj.m_Einheit = DBIEinheit; DBrecObj.m_Wert = DBIWert; DBrecObj.m_Grundlage = DBIGrundlage; DBrecObj.m_Alternative = DBIAlternative; DBrecObj.m_Kategorie = DBIKategorie; DBrecObj.m_Studie = DBIStudie; DBrecObj.m_CahraFaktor = DBICahraFaktor; DBrecObj.m_Resourceextr = DBIResourceextr; DBrecObj.m_Luftemission = DBILuftemission; DBrecObj.m_Frischwasser = DBIFrischwasser; DBrecObj.m_Seewasser = DBISeewasser; DBrecObj.m_Landwboden = DBILandwboden; DBrecObj.m_Industboden = DBIIndustboden; DBrecObj.m_Landnutz = DBILandnutz; } void DBInterface::suchen() { DBrecObj.m_strFilter = Suchstr; DBrecObj.Requery(); } void DBInterface::suchenSubst(CString Such) { Suchstr += "Substanz Like '%" + Such +"%'"; suchen(); } [/cpp]
Das war mein erster Code zu dem Projekt und der hat auch ganz gut funktioniert.
Auf meine Tabelle greif ich zu, indem ich in der Interfaceklasse ein Objekt meiner von CRecordset abgeleiteten Klasse erzeuge.
Ich hab jetzt in meinem neuen Code aber nicht nur die eine Tabelle sondern eben acht, wollte das ganze aber wieder einigermaßen genauso gestalten. Das gute hier ist, dass ich keine View benötige um mit der Datenbank zu arbeiten.
(Ich hab die Funktionen über die Kommandozeile ausprobiert und es hat geklappt)
Jetzt wisst ihr wie ich mir das denke, dass es funktioniern könnte.
-
pinorrix schrieb:
wenn du ne loesung zum dynamischen verwalten vieler beliebiger tabellen hast, bin ich interessiert....
Mmmmhhhh, dynamisch.... ?!?
Ich hatte mal sowas (muss ich mal ein bischen anonymisieren, hoffentlich geht dadurch nix verloren...):
Erstmal braucht man mal ein CDatabase-Objekt:CDatabase* dbObj;
Da kommt bei mir der ganze Datenbank-Kram rein (Passwort usw.).
Dann noch ein Recordset-Objekt:CRecordset RecSet(&dbObj);
CStringvariablen für Abfrage und Abfrageergebnis:
CString strSQL = "SELECT MAX(FELD) AS RESULT FROM TABELLE"; // Kann natürlich auch dynamisch zusammengestellt werden CString strResult;
Und los gehts:
RecSet.Open(CRecordset::forwardOnly, strSQL, CRecordset::readOnly); if(!RecSet.IsEOF()) RecSet.GetFieldValue("RESULT", strResult); AfxMessageBox(strResult); // zum Testen, Ergebnis steht in strResult RecSet.Close();
Könnte vielleicht sogar klappen...
-
HI Ihr,
ich hab was gefunden, wie es klappen könnte. Es wird vielleicht ein wenig
umfangreich aber ich denke, dass es auch für andere interessant ist.
Erstmal habe ich mir mit Access eine neue Datenbank zum Rumspielen gebastelt.Sie besteht aus drei Tabellen:
Beitraege
Beitragsnummer
MitgliedsnummerMitglieder
Mitgliedsnummer
Name
OrtPersonendetails
Personennummer
Mitgliedsnummer
Name
VornameVerknüpfungen in Access:
Mitglieder.Mitgliedsnummer 1:n Beitraege.Mitgliedsnummer
Mitglieder.Mitgliedsnummer 1:n Personendetails.MitgliedsnummerSoweit steht schon mal die Datenbank.
Dann hab ich in der MSDN gelesen, dass man Tabellen in ein Recordset einfügen
und dort mittels m_strFilter verknüpfen kann. Dazu müsse man dann händisch die
RFX Funktionen in der DoFieldExchange-Funktion vervollständigen usw...Voraussetzung für das folgende ist, dass die datenbank als ODBC-Daenbank dem
System bekannt ist.Ich hab jetzt mit dem Assistenten eine von CRecordset abgeleitet Klasse
erzeugt.Wenn man dieses Recordset mittels Assistent mit einer ODBC-Datenquelle
verbindet, fragt er welche Tabellen er in das Recordset einfügen soll.
Dort hab ich dann probiert alle Tabellen auszuwählen und als ich die STRG-Taste
gedrückt hielt, ließen sich alle Tabellen aus meiner Datenbank mit der Maus
auswählen.
Anschließend hab ich fertig gedrückt und zack hatte ich alle Tabellen in meinem
einen Recordset.Das sieht dann folgendermaßen aus:
IMPLEMENT_DYNAMIC(Dabarec, CRecordset) Dabarec::Dabarec(CDatabase* pdb) : CRecordset(pdb) { //{{AFX_FIELD_INIT(Dabarec) m_Beitragsnummer = 0; m_Mitgliedsnummer = 0; m_Betrag = 0; m_Feld1 = _T(""); m_Mitgliedsnummer2 = 0; m_Name = _T(""); m_Ort = _T(""); m_Personennr = 0; m_Mitgliedsnummer3 = 0; m_Name2 = _T(""); m_Vorname = _T(""); m_nFields = 11; //}}AFX_FIELD_INIT m_nDefaultType = dynaset; } CString Dabarec::GetDefaultConnect() { return _T("ODBC;DSN=Mitglieder"); } CString Dabarec::GetDefaultSQL() { return _T("[Beitraege],[Mitglieder],[Personendetails]"); } void Dabarec::DoFieldExchange(CFieldExchange* pFX) { //{{AFX_FIELD_MAP(Dabarec) pFX->SetFieldType(CFieldExchange::outputColumn); RFX_Long(pFX, _T("[Beitragsnummer]"), m_Beitragsnummer); RFX_Long(pFX, _T("[Beitraege].[Mitgliedsnummer]"), m_Mitgliedsnummer); RFX_Long(pFX, _T("[Betrag]"), m_Betrag); RFX_Text(pFX, _T("[Feld1]"), m_Feld1); RFX_Long(pFX, _T("[Mitglieder].[Mitgliedsnummer]"), m_Mitgliedsnummer2); RFX_Text(pFX, _T("[Mitglieder].[Name]"), m_Name); RFX_Text(pFX, _T("[Ort]"), m_Ort); RFX_Long(pFX, _T("[Personennr]"), m_Personennr); RFX_Long(pFX, _T("[Personendetails].[Mitgliedsnummer]"), m_Mitgliedsnummer3); RFX_Text(pFX, _T("[Personendetails].[Name]"), m_Name2); RFX_Text(pFX, _T("[Vorname]"), m_Vorname); //}}AFX_FIELD_MAP }
In die DoFieldExchange übernimmt der Assistent automatisch die zugehörigen
Spalten aller drei Tabellen und macht Sie in den Membervariablen verfügbar.
Außer für die Spalten Mitgleidsnummer legt er jetzt nummerierte Variablen an.
Man muß dazu einfach in der DoFieldExchange gucken zu welcher Tabelle diese
dann gehören.
z.B.:
Tabelle ist Beitraege:RFX_Long(pFX, _T("[Beitraege].[Mitgliedsnummer]"), m_Mitgliedsnummer);
Tabelle ist Mitglieder:
RFX_Long(pFX, _T("[Mitglieder].[Mitgliedsnummer]"), m_Mitgliedsnummer2);
Jetzt ist noch die Frage, wie man die in Access schon verknüpften Daten weiter
als verknüpfte Daten behandelt und wie man sie zugänglich macht.Dieser Schritt ist auch recht gut zu verstehen.
Man erzeugt als nächstes ein Objekt von dem mit dem Assisenten angelegten
Recordset.
Die Reihenfolge ist jetzt wichtig:Dann erzeugt initialisiert man m_strFilter
und übergibt damit die Verknüpfung an die Datenbank oder das Recordset( habs
auch noch nich 100% kapiert)
und danach öffnet man erst die Datenbank(Recordset) mit Open.
Verknüpfungen stellt man folgendermaßen her:Dabarec DB; DB.m_strFilter = "Mitglieder.Mitgliedsnummer = Beitraege.Mitgliedsnummer AND Mitglieder.Mitgliedsnummer = Personendetails.Mitgliedsnummer AND Personendetails.Name ='irgendeinName'"; DB.Open();
Ich habe hier m_srtFilter zwei Verknüpfungen übergeben:
1. : Mitglieder.Mitgliedsnummer = Beitraege.Mitgliedsnummer
Verknüpfung der Tabelle Mitglieder mit der Tabelle Beitraege über
Mitgliedsnummer2. : Mitglieder.Mitgliedsnummer = Personendetails.Mitgliedsnummer
Verknüpfung der Tabelle Mitglieder mit der Tabelle Personendetails über
Mitgliedsnummerdie Vernüpfungen sind SQL-mäßig mit AND verbunden.
Außerdem habe ich noch eine Filteranweisung übergeben:
Personendetails.Name ='irgendeinName'Das Recordset enthält dann alle Daten, die mit irgendenName über die
Mitgliedsnummer verknüpft sind, d.h. auch wenn man z.B. nur nach Schulz sucht,
zeigt die Ausgabe der Datensätze alle Personen mit dem Namen Schulz an, ihre
Vornamen und die Beiträge, die von Personen mit Namen Schulz bezahlt wurden.
(Im Prinzip füllt man mit m_strFilter eine große Tabelle(siehe
Klassendefinition), in die durch die Verknüpfung und das Filterkriterium alle
mit Schulz zusammenhängenden Daten eingetragen werden und dann ausgelesen
werden können)nicht vergessen:
DB.Close();
Ich hoffe es hat jemandem geholfen.
Was mich jetzt noch interessiert, wäre wie ich mit AddNew() Daten in eine
meiner Tabellen einfügen kann.Muß ich dazu m_strFilter leer lassen und dann
einfach meine Membervariablen, in die ich was geschrieben hab mit Update
aktualisieren?
Wenn m_strFilter mit der Verknüpfung gestartet wird klappt es nämlich nicht
einfach so mal mit Update() z.B. in die Tabelle Beitraege eine neue Einzahlung
einzufügen.Weiß da jemand Bescheid??
Tschü
-
Hallo
Ich beziehe mich jetzt auf den Code meiner Probierdatenbank( Mitgliedertabelle,
Beitragstabelle, Personentabelle).
Ich hab ja geschreiben, daß ich alle drei Tabellen aus der Datenbank in einem
Recordset(Recordset 1) habe.
Ich hab nun folgende Probleme und weiß nicht, was ich da falsch mache oder warum bestimmte Effekte auftreten.
Ich habe mir für die Tabelle Beiträge ein einzelnes Recordset(Recordset 2) noch
zusätzlich in meine Anwendung eingebaut, da es mit AddNew() nicht möglich war
Daten in eine einzelne dieser Tabellen in Recordset 1 zu schreiben.
Mit Recordset 2 kann ich ohne Probleme Daten in die Tabelle Beitraege eintragen
und sie sind in Recordset 2 auch sichtbar.
Das Problem ist, das die Daten jetzt in Recordset 2 stehen und auch ohne
Probleme mit AddNew usw in die Access-Datenbank eingefügt werden, ich aber auch
wenn ich das Recordset 1(enthält auch die Tabelle Beitraege plus zwei weiterer
Tabellen) mit den drei Tabellen mittels close schließe und wieder öffne nicht
die vorher eingegebenen Daten angezeigt, ausgegeben bekomme.(Ich benutze keine
View sonder las es mir an der Konsole ausgeben)
An m_strFilter(Recordset 1) habe ich nichts geändert.
Das komische ist jetzt außerdem, dass wenn ich die gesamte Anwendung schließe
und wieder neu starte, ich die vorher in Recordset 2 eingegebenen Daten auch in
Recordset 1 finde.
Woher kommt das?
Ich wäre für jede Hilfe oder Anregung sehr dankbar, da ich bis jetzt weder in
der MSDN noch im Forum etwas dazu finden konnte.
Vielen Dank im VorausHermie
-
Falls es jemanden interessiert:
nach langem basteln mach ich es jetzt so, daß ich zwei Klassen als Interface
eingefügt hab, welche durch Memberfunktionen meine beiden Recordsets verwalten.
In der einen Interfaceklasse sind dann die Methoden zum Suchen und Verknüpfen
mit m_strFilter und in der anderen Interfaceklasse Methoden um Datensätze in
die Tabellen einzufügen.
Ich hoff mal ich konnte jemandem damit helfen.Tschü
Sorry für die lange Labberei vorher!
-
hi hermie
kennst du dich mit "Datenbanken per ADO bearbeiten" aus ?
Bei mir geht das Hinzufügen zur Datenbank nicht.
Das geht doch auch mit der AddNew Funktion..
danke
-
hi Stgt,
ich hab mal gegoogelt und das dabei gefunden:
http://www.aspheute.com/artikel/20010213.htm
Ansonsten weiß ich auch nicht, wie man es machen sollte.
Ich hab grad angefangen mich mit Datenbanken zu beschäftigen und bin froh,
das ich meine einfach über ODBC ansprechen kann.
Aber AddNew() ist glaube ich schon richtig.
Viel Glück!!
-
okay danke ,werd's mir gleich anschauen...:-)
danke, dir auch viel Erfolg