[C++Builder] Hilfe zu ADO-Abfragen



  • Schau mal ich habe folgenden Teil geändert:

    adoq->SQL->Clear();
    adoq->SQL->Add("SELECT a.datum FROM aida_daten a");
    adoq->SQL->Add("WHERE a.personalnummer = (:Persnr)");
    adoq->Parameters->ParamByName("Persnr")->Value  = "85/150";
    adoq->Open();
    

    Er sagt mal keinen Syntaxfehler, aber ich vertraue mal auf dein Wissen. Kann das so funktionieren?

    Nach dem "adoq->Open()" wird immer noch keine Ergebnismenge geliefert.
    Ich habe es vorher schon mal geschrieben und zwar wird ein ein "Hint" gezeigt wenn ich mit der Maus über den Programmcode "adoq->Open()" gehe.
    Dieser hat folgende Nachricht:

    adoq->Open() = E2027 Eine Speicheradresse muss verwendet werden

    Muss ich mich um diese Meldung kümmern oder sagt diese nicht wichtiges aus?



  • SO hier mal mein kompletter Code:

    #include <vcl.h>
    #pragma hdrstop
    
    #include "Unit1.h"
    #include "DB.hpp"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    
       AnsiString DB_PROVIDER      =     "MSDASQL.1";
       AnsiString DB_PASSWORD      =     "root";
       AnsiString DB_USER_ID       =     "root";
       AnsiString DB_DATA_SOURCE   =     "Time_Server";
    
       adoc->ConnectionString =  "Provider="+DB_PROVIDER+";Password="+DB_PASSWORD+";"
                                 "User ID="+DB_USER_ID+";Data Source="+DB_DATA_SOURCE+";"
                                 "Persist Security Info=True;";
    
       adoc->Open();
    
       adoq->DataSource = ds;
       adoq->Connection = adoc;
    
       AnsiString sql;
       AnsiString ergebnis;
       AnsiString persnr = "85/150";
       int count;
    
       //sql = "SELECT bs.emailsperre FROM Bearbeitungsstatus bs WHERE bs.personalnummer = 85/150 AND bs.monat = 3 AND bs.jahr = 2006";
       //sql = "select * from Mitarbeiter m where m.personalnummer = 85/150";
       //sql = "select a.datum from aida_daten a where a.personalnummer = '"+persnr+"'";
    
       adoq->SQL->Clear();
       adoq->SQL->Add("SELECT a.datum FROM aida_daten a");
       adoq->SQL->Add("WHERE a.personalnummer = (:Persnr)");
       adoq->Parameters->ParamByName("Persnr")->Value  = "85/150";
       adoq->Open();
    
       count = adoq->RecordCount;
    
    }
    //---------------------------------------------------------------------------
    

    Hoffe man sieht jetzt mehr! 🙂



  • Und hier auch nochmal die Klasse:

    #ifndef Unit1H
    #define Unit1H
    //---------------------------------------------------------------------------
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    #include <Forms.hpp>
    #include <ADODB.hpp>
    #include <DB.hpp>
    //---------------------------------------------------------------------------
    class TForm1 : public TForm
    {
    __published:	// Von der IDE verwaltete Komponenten
       TButton *Button1;
       TADOQuery *adoq;
       TDataSource *ds;
       TADOConnection *adoc;
       TADODataSet *adod;
       void __fastcall Button1Click(TObject *Sender);
    private:	// Anwender-Deklarationen
    public:		// Anwender-Deklarationen
       __fastcall TForm1(TComponent* Owner);
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TForm1 *Form1;
    //---------------------------------------------------------------------------
    #endif
    


  • Natürlich habe ich nicht Deine DB.

    Also habe ich mal folgendes gemacht:

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
       TADOQuery *adoq = new TADOQuery(this);
       TADOConnection *adoc = new TADOConnection(this);
    
       adoc->ConnectionString =  "...MeinConnectionStringAufMeineDB...";
    
       //adoq->DataSource = ds; //Hier erschließt sich mir der (Un)sinn nicht
       adoc->Open();
    
       adoq->Connection = adoc;  //wahnsinnig aussagekräftige & unterscheidbare Variablen adoc<->adoq
    
       AnsiString sql;
       AnsiString ergebnis;
       int count;
    
       if (adoq->Active)
            adoq->Close();  // immer schön vorsichtig sein
    
       adoq->SQL->Clear();
       adoq->SQL->Add("SELECT AUFTRAG FROM AUFTRAEGE a");
       adoq->SQL->Add("WHERE a.auftrag = (:Auftrag)");
       adoq->Parameters->ParamByName("Auftrag")->Value  = "TEST";
       adoq->Open();
    
       count    = adoq->RecordCount;
       ergebnis = adoq->FieldByName("AUFTRAG")->AsString;
    
       Edit1->Text= count;
       Edit2->Text= ergebnis;
    }
    

    wenn ich auf den Knopf drück (didel didel dum) ->
    Ergebnis ist 1, "TEST". 👍

    Also mein C++ -Code ist funktionsfähig. -> Dein Code sollte auch funktionieren (wenn Du den Quatsch mit der DataSource wegläßt), vorausgesetzt, Dein Select ist OK .



  • Ach ja:
    Da Du die Connection und die Query wo anders erstellst,
    ist

    if (adoc->Connected)
       adoc->Close();
    
    //&
    if (adoq->active)
       adoq->Close();
    

    Pflicht. 😉 Du kannst ja schon vor Event-ButtonClick alles mögliche damit anstellen.

    Und ja ich verschreib mich dauernd mit adoq <->adoc, also aufpassen. 😃



  • JA wunderbar!
    Es funktioniert! JUHU! 😃

    Vielen Dank für deine Hilfe und deine Geduld. Es hat sich aber gelohnt!

    Die ADO-Komponenten müssen erst meine Freunde werden *g* aber das ist schon mal ein guter Anfang!



  • Ich hab noch eine kleine Frage! 🙄

    Wenn ich nun eine Ergebnismenge bekomme die mehrere Einträge hat, wie kann man am besten diese Werte an andere Methoden übergeben.

    Ich kann ja z.B. einen Pointer auf das TADOQuery Objekt übergeben wobei ich das nicht so toll finde.

    Gibt es eine Komponente oder Struktur, in die man ganz leicht alle Werte er Ergebnismenge kopieren kann und die dann als return - Wert übergibt?

    Ich habe mir auch schon überlegt, alle Werte in einem AnsiString zu speichern und diesen dann zu übergeben, doch dann muss ich den an der anderen Stelle wieder auseinander filtern.

    Schön wäre eine Liste, mit der man leicht auf alle Werte drauf zugreifen kann!

    Eine Idee??



  • Stellt sich mir die Frage, wozu Du die Ergebnismenge in einer Liste haben willst?!? Was willst Du mir dieser Liste machen?

    Im Prinzip ist die Query die Liste.



  • Das schöne an einem DataSet ist, daß der Container so variabel ist. Du mußt nicht allzu viel über die Datenmenge wissen, die dort "aufbewahrt" wird.

    Klar kannst Du den ganzen Kram an einen StringArray übergeben, doch da mußt Du mindestens die Struktur des RecordSets kennen, nachbilden und bei jeder Änderung des DataSets mit ändern. Vieeel Arbeit für welchen Nutzen?

    Warum sollte ein DataSet nicht mehrere Datensätze enthalten (ja ich mische hier C++ und SQL Terminologie wild durcheinander). Angezeigt wird in einen DBEdit immer der aktuelle DS oder alle DS der Abfragemenge in einem DBGrid (einer Tabelle mit Bezug auf eine DataSource mit Bezug auf ein DataSet z.B. BDGrid->DataSource->DataSet->FieldByName("DaskommtMirBekanntVor"))

    Navigieren kannst Du durch das DataSet mit (also den aktuellen DS wählen/ändern):
    RecNo = Aktueller DS
    First = Erster
    Last = Letzter
    Next = Nächster
    Prior = Vorheriger
    etc. uvm. (siehe BCB-Hilfe Methoden TDataSet) :p



  • Morgen,

    ich habe mein Programm so aufgebaut, das es keine visuellen Anzeigen geben wird. Das programm wirr im HIntergrund laufen und Aufgaben erledigen.

    Ich habe eine Klasse die z.B. folgende Methode hat:

    ->ueberpruefe_Akzeptanz();

    Mit dieser Methode möchte ich einen Wert aus der Datenbank holen und überprüfen welche Information der Wert mir leifert.

    In der Methode ueberpruefe_Akzeptanz() wird ein SQL Befehl an die folgende Methode gegeben.

    ->sql_statement_ausfuehren(AnsiString sql_befehl)

    und hier möchte ich dann die Sache durchführen die wir hier in diesem Thread so toll besprochen haben!
    Wenn es ein SQL-Befehl ist, der nur Sachen in die DB schreibt, dann ist das ja kein Problem, da keine Ergebnismenge doch gerade bei diesem Beispiel (->ueberpruefe_Akzeptanz() ) will ich ja eine Ergebnismenge zurückgekommen. Das heißt die Methode sql_statement_ausfuehren muss mir die Ergebnismenge an die Methode ->ueberpruefe_Akzeptanz() geben damit ich mit dieser was machen kann.

    Meine Frage ist jetzt eh da ich schon das ganz getestet habe, warum er mir nicht alle Werte des SQL-Befehls liefert.

    Wenn ich z.B. den Befehl:

    select * from Mitarbeiter;
    

    ausführe (DB-Explorer), dann bekomme ich wirklich alle Werte angezeigt. Wenn ich das aber jetzt mit der ADOQuery-Koponente mache, dann bekomm ich leider nicht alle Werte geliefert, sondern nur eine willkürlichen Teil.

    Ist das normal??

    Wie kann ich denn alle Werte bekommen??

    Danke für die Antworten



  • Ohne Where-Bedingung hat er alle Werte der Tabelle zu liefern, so oder so.

    Bist Du sicher, daß Du immer auf die gleiche Datenbank zugreift. Das ist mein beliebtester Fehler um bei gleichem Select unterschiedliche Ergebnismengen zu erhalten.

    Alternativ dazu steht immer im Raum, daß der Select doch nicht identisch ist.
    (z.B. ein vergessener Parameter)



  • Es ist die gleicher Datenbank!
    Er leifert ja einen Teil von den Ergebnissen die er bringen soll.

    Wenn ich im DB-Explorer folgenden SQL-Befehl eingebe:

    select * from Mitarbeiter;

    dann bekomme ich 5*4 = 20 Ergebnisse (ist nur eine Test-DB *g*)

    Dieses Ergebnis erwarte ich auch jetzt mit der ADOQuery-Koponente.

    Wenn ich den gleichen SQL-Befehl

    AnsiString sql;
    sql = "select * from Mitarbeiter";
    
    sql_statement_ausfuehren(sql);
    

    jetzt mit der ADOQuery ausführe, dann liefert mir adoquery->RecoredCount = 5 und
    das Ergebnis ist die erste Zeile der Ergebnismenge. Es sollten aber noch 4 weitere Zeilen folgen 😕

    Eine Ahnung an was das liegen kann?

    Hier jetzt mal die definierte sql_statement_ausfuehren Methode:

    Test::sql_statement_ausfuehren(AnsiString sql_befehl)
    {
    
       TADOQuery *adoq = new TADOQuery(Application);
       TADOConnection *adoc = new TADOConnection(Application);
    
       AnsiString DB_PROVIDER      =     "MSDASQL.1";
       AnsiString DB_PASSWORD      =     "root";
       AnsiString DB_USER_ID       =     "root";
       AnsiString DB_DATA_SOURCE   =     "Time_Server";
    
       adoc->ConnectionString =  "Provider="+DB_PROVIDER+";Password="+DB_PASSWORD+";"
                                 "User ID="+DB_USER_ID+";Data Source="+DB_DATA_SOURCE+";"
                                 "Persist Security Info=True;";
    
       adoc->Open();
    
       adoq->Connection = adoc;
    
       AnsiString sql;
       AnsiString ergebnis;
       int count;
    
        if (adoq->Active)
        {
          adoq->Close();
        }
    
       adoq->SQL->Clear();
       adoq->SQL->Add(sql_befehl);
       adoq->Open();
    
       count = adoq->RecordCount;
    
       AnsiString Ergebnis = "";
    
       Ergebnis += IntToStr(count) + ";";
    
       for(int i=0; i < count ; i++)
       {
          Ergebnis += adoq->Fields->Fields[i]->AsString;
          Ergebnis += ";";
    
       }
    
    }
    


  • Deine for-Schleife kommt mir dubios vor.
    Folgendes Beispiel liest die einzelnen Datensätze aus der Tabelle. In der Variable Ergebnis werden die einzelnen Felder zusammengekettet.

    while (!adoq->Eof)
    {
       Ergebnis = adoq->FieldByName("Feldname1")->AsString;
       Ergebnis = Ergebnis + "," + adoq->FieldByName("FeldnameX")->AsString;
       adoq->Next();
    }
    


  • dann bekomme ich 5*4 = 20 Ergebnisse (ist nur eine Test-DB *g*)

    kann es sein dass die 4 in deinem Beispiel die Anzahl der Spalten ist?

    das Query liefert dir Datensätze und jeder Datensatz besteht aus einer tabellenabhängigen Anzahl von Spalten

    ich glaube du weißt immer noch nicht wie Query funktioniert



  • Das scheint schon eine gute Lösung zu sein, doch was mich bis jetzt noch ein bisschen daran stört ist die Methode

    adoq->FieldByName("....")->AsString;
    

    denn woher soll ich denn z.B. bei einem SQL-Befehl wie "select * from Mitarbeiter" wissen, welche Spalten er da alle findet? Ich muss die ihm ja dann immer als String in die Methode übergeben, oder?

    Ich sehe das so, das die Methode ->FieldByName("...") immer den Ausdruck braucht, den ich nach dem select Statement benutze, also z.B.

    select vorname from ---> FieldByName("Vorname");

    oder

    select Persnr from ----> FieldByName("Persnr");

    wie mach ich das dann aber in einer Schleife und vor allem mit dem SQL Befehl:

    select * from... ??????

    Das habe ich noch nicht richtig verstanden!



  • @Linnea:

    Du hast vollkommen recht das ich mich noch nicht mit dieser Koponente auskenne!
    Ich war der Meinung die Query-Komponente führt SQL-Befehle aus und speichert alle Ergebnisse in sich.

    So wie ich dich gerade verstanden habe speichert sie immer nur eine Zeile ab, oder?

    Die 4 ist die Spaltenanzahl, richtig! Ich habe 4 Spalten und zu denen gibt es 5 Zeilen mit Werten.
    Kann ich jetzt trotzdem mit einem SQL-Befehl an alle Werte ran kommen?
    Denn ich benötige ja alle!



  • Die for-Schleife aus deinem Beispielcode erfüllt doch den gewünschten Zweck. EDIT: Also deine for schleife in meine while Schleife. Nur RecordCount musst du durch FieldCount ersetzen.

    Ich bevorzuge halt den Zugriff über den Namen der Spalte. Die Spaltennamen kann man auch bei 'select * from' nutzen. Select * würde ich aber auch vermeiden und alle Felder auflisten, denn nur was man explizit hinschreibt, ist auch eindeutig.



  • Nachdem, was ich gelesen habe, ist das Ergebnis des Selects immer das gleiche
    jeweils 5 Datensätze je x(4) Spalten.

    Ein select * ist für adhoc-Abfragen OK. Für eine Hinterlegung in Code ist eigentlich immer Blödsinn. Was passiert, wenn Du in einem halben Jahr der Tabelle noch weitere Spalten hinzufügst? Willst Du diese Spalten dann sofort in der Abfrage haben. Hier sehe ich einen konzeptionellen Fehler.

    Diesen hat bei uns auch mal ein Praktikant begangen, Schreib- und Denkarbeit gespart und nach kurzer Zeit funktionierte sein Programm nicht mehr.

    Zweitens solltest Du Dich dringend mit der Hilfe befassen & Du solltest Dich mit den Grundlagen von (relationalen) Datenbanken befassen.

    Wenn Du einen Ergebnisstring willst, kannst Du das auch einfacher haben!

    Bsp.

    select Spalte1 + ';'+Spalte2+'___'+convert(varchar(120),Spalte3)+'irgendEineStringKonstante' as Ergebnis from Tabelle
    

    Das geht schneller als es in C++ nachzustellen. Für derartige Datenoperationen ist SQL ja schließlich entworfen und optimiert.



  • Also in Sachen Datenbanken bin ich leider noch ein Anfänger. Einige Sachen habe ich von den Vorlesungen mitbgenommen, doch die müssen sich in der pratischen Arbeit erst noch festigen und setzen.

    Den SQL-Befehl: "select * from Mitarbeiter" war nur als kleinen test gedacht, damit ich mal sehen, wie die Query_koponente die Ergebnisse verwaltet und wie ich auf sie zugreifen kann.
    In den meisten Fällen werde ich in meinem programm nach einem Ergebnis abfragen und nicht eine große Menge!
    Doch trotzdem kommt es an manchen Stellen vor und ich möchte halt damit umgehen können um auf alle Daten zugreifen zu können.

    Die BCB-hilfe ist an manchen Stellen recht gut und verständlich und hilft auch sehr weiter doch manchmal (gerade in diesem Fall) werden Begriffe benutzt die bei mir einen anderen Sinn haben.

    Also ich versuche mal mit den Erkenntnissen dieses Threads zurecht zukommen. Doch wenn noch Fragen sind hoffe ich ich kann mich wieder an euch wenden 😃

    !?


Anmelden zum Antworten