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 an 😮

    Ich hab mir das aber nicht ausgedacht 😉

    Ich hoffe mal ich konnte das jetzt rüberbringen, was mein Problem ist.
    Ich hoffe auf gute Antworten 😉

    Hermie



  • 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
    Mitgliedsnummer

    Mitglieder

    Mitgliedsnummer
    Name
    Ort

    Personendetails

    Personennummer
    Mitgliedsnummer
    Name
    Vorname

    Verknüpfungen in Access:

    Mitglieder.Mitgliedsnummer 1:n Beitraege.Mitgliedsnummer
    Mitglieder.Mitgliedsnummer 1:n Personendetails.Mitgliedsnummer

    Soweit 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
    Mitgliedsnummer

    2. : Mitglieder.Mitgliedsnummer = Personendetails.Mitgliedsnummer
    Verknüpfung der Tabelle Mitglieder mit der Tabelle Personendetails über
    Mitgliedsnummer

    die 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 Voraus

    Hermie



  • 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 😉


Anmelden zum Antworten