[C++Builder] Hilfe zu ADO-Abfragen
-
Hmm wenn ich ehrlich bin hat mir die Lösung mit dem "FieldByName" wirklich nicht so gut gefallen!
Aber deine Argumente scheinen für diese Methode zu sprechen!
Wahrscheinlich hast du recht mit der where Bedingung!
Ich habe gedacht, dass wenn ich schreibe:
"SELECT bs.emailsperre FROM Bearbeitungsstatus bs WHERE bs.personalnummer = 85/150 AND bs.monat = 3 AND bs.jahr = 2006"
das er dann alles als String ansieht und diesen dann ausführen kann!
Müssen also Werte nach denen gesucht werden immer in Hochkomma stehen?Wie würde ich das in diesem Fall richtig machen?
Wenn ich z.B. ein anderes SQL-Statement nehme wie:
AnsiString persnr = "67/150"; sql = "select a.datum from aida_daten a where a.personalnummer = "+persnr;
dann führt er sogar nicht mehr die Methode adoquery->Open() aus!
Er meldet EDatabaseError -> Ungültige Feldgröße!Was hat das denn jetzt schon wieder zu bedeuten? Alle SQL-Befehle teste ich vorher im DB-Explorer und die funktionieren super.
Das einzige ist, das es mit diesem Hochkommas zusammenhängen kann!
Wie kann ich das lösen?
-
Hast Du mal den Debugger benutzt und dabei den Ausdruck sql überwacht?
Welchen Inhalt hat sql, wenn Du es mit <<ADOQuery->SQL->ADD(sql);>> übergibst?
Du kannst auch den Ausdruck <<ADOQuery->SQL>> selbst überwachen und sehen, was da rauskommt.
Ich hätte übrigens auf folgende Syntax für den SQL-Befehl getippt.
SELECT bs.emailsperre FROM Bearbeitungsstatus bs WHERE bs.personalnummer = '85/150' AND bs.monat = 3 AND bs.jahr = 2006"
Also z.B. so zusammengebaut
AnsiString persnr = "67/150"; AnsiString sql = "select a.datum from aida_daten a where a.personalnummer = '"+persnr+"'";
Eleganter wäre natürlich die Übergabe von Parametern an das SQL-Konstrukt(nachzulesen in BCB-Hilfe).
So und nun mach ich erst mal Mittag.
-
Also ich hab schon jetzt des öffteren gedebuggt und bekomme folgenden String der AnsiString-Variablen zugewiesen:
"select a.datum from aida_daten a where a.personalnummer = '85/150'"
So müsste es ja eigentlich stimmen oder?
Er bringt aber trotzdem keine Ergebnismenge!Woran kann das denn liegen?
-
Poste bitte mal den Code-Ausschnitt von der Übergabe (Also Zusammenbau SQL-String) über das Öffnen bis zur Abfrage der Ergebnismenge.
Sonst bleibt das ganze doch bloß Bäume suchen im Wald.
Mach den Auschnitt lieber zu groß als zu klein.
-
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,
istif (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 folgenEine 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!
-
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!