Entwickler-Ecke

Datenbanken - SQL bei zwei Tables


hansa - Do 05.12.02 19:42
Titel: SQL bei zwei Tables
Hi,

wie mach ich das eigentlich, wenn zu einer Table genau die Werte aus einer anderen benötigt werden ? Ich brauche bei einer Rechnung zu jeder Rechnungsposition den richtigen Artikel. Habe ich jetzt die Rechnungs-DataSet bringt er mir mit folgendem immer denselben Artikel:

Quelltext
1:
2:
3:
    WITH RecDataMod.ArtDS DO
      SelectSQL.Text := 'SELECT * FROM ART WHERE ID = ' +
      RecDataMod.RecDS.FieldByName ('ID_ART').AsString;

Das soll keine Lookup-Box werden sondern eine 1:1 Beziehung.

Gruß
Hansa


Alfons-G - Do 05.12.02 20:09

:wave:
Auf diese Art bekommst Du immer nur denselben Artikel, nachdem Du ein festes Argument übergibst. Du must Dich durch das Rechnungs-Dataset bis zum Ende durchbewegen:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
while not RecDataMod.RecDS.Eof do
  begin
  WITH RecDataMod.ArtDS DO 
      begin
      SelectSQL.Text := 'SELECT * FROM ART WHERE ID = ' + 
      RecDataMod.RecDS.FieldByName ('ID_ART').AsString;
  ...
  end;
  RecDataMod.RecDS.Next;
end;
So bekommst Du alle Artikel ;)

:idea:


hansa - Do 05.12.02 20:15

Hi Alfons,

ja genau das wars. Der Witz ist, daß ich es vorher bei anderen Tables so gemacht habe und jetzt nicht mehr wußte wie. Das ist schon länger her und ich finde den Quelltext nicht. :mrgreen:

Gruß
Hansa


hansa - Do 05.12.02 20:37

Nee,

zu früh gefreut. :shock: Bei meinem Beispiel hat er mir nur den ersten Artikel geliefert. Mache ich es so :

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
    RecDataMod.RecDS.First;
    while not RecDataMod.RecDS.Eof do begin
      WITH RecDataMod.ArtDS DO begin
{
        RecDataMod.ArtDS.active := false;
}
        SelectSQL.Text := 'SELECT * FROM ART8 WHERE ID = ' +
        RecDataMod.RecDS.FieldByName ('ID_ART').AsString;
{
        RecDataMod.ArtDS.active := true;
}
      END;
      RecDataMod.RecDS.Next;
   end;


Dann kriege ich nur den letzten Artikel. Seltsamerweise habe ich aber mit diesem hier :

Quelltext
1:
2:
3:
4:
    WITH RecDataMod.RecDS DO
      SelectSQL.Text := 'SELECT * FROM REC8 WHERE ID_RECKOPF = ' +
      RecDataMod.RecKopfDS.FieldByName ('ID').AsString;
    RecDataMod.RecDS.active := true;


die richtigen 4 Rechnungspositionen aus der DB gefischt. Die erkenne ich am Preis, der bei den Positionen hinterlegt ist.

Gruß
Hansa


Alfons-G - Do 05.12.02 21:23

Du hast bei Deinem nicht funktionierenden Beispiel das vergessen:RecDataMod.RecDS.Next;Dann klappt's.

;)


hansa - Fr 06.12.02 00:22

Hi Alfons,

hmm, tja da häng ich genau fest (Siehe weiter oben).


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
RecDataMod.RecDS.First; 
    while not RecDataMod.RecDS.Eof do begin 
      WITH RecDataMod.ArtDS DO begin 
        SelectSQL.Text := 'SELECT * FROM ART8 WHERE ID = ' + 
        RecDataMod.RecDS.FieldByName ('ID_ART').AsString; 
      END; 
      RecDataMod.RecDS.Next;    ******************************
   end


Sehe logisch keinen Fehler, wo könnte denn da was fehlen :?:

Gruß
Hansa


LCS - Fr 06.12.02 08:55

Hi
ich würde sagen es muss so aussehen:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
RecDataMod.RecDS.First; 
    while not RecDataMod.RecDS.Eof do begin 
      WITH RecDataMod.ArtDS DO begin 
        SelectSQL.Text := 'SELECT * FROM ART8 WHERE ID = ' + 
        RecDataMod.RecDS.FieldByName ('ID_ART').AsString; 
        Active := True;
      END; 
      RecDataMod.RecDS.Next;    ****************************** 
      RecDataMod.ArtDs.Active := False;
   end

Allerdings finde ich, dass es auch einfacher geht. Möglichkeit 1: Du erweiterst die SQL von RecDs so, dass du bereits hier mit JOIN die beiden Tabellen verbindest. Dann müsstest du nur mit einer Datenmenge arbeiten.

Möglichkeit 2: Du änderst die SQL von ArtDs ab in:

Quelltext
1:
SELECT * FROM ART8 where ID = :ID_ART                    

Anschliessend weisst du der Eigenschaft DataSource von ArtDs die Datasource zu, die an RecDs hängt. Damit wird bei jeder Bewegung in RecDs die ArtDs entsprechend positioniert und du sparst dir das alles.

Gruss Lothar


hansa - Fr 06.12.02 13:26

Hi,

Zitat:
Dann müsstest du nur mit einer Datenmenge arbeiten.


Glaube, daß das sowieso besser wäre. Der Rechnungsempfänger, obwohl er keine Ahnung hat, sieht im Prinzip jede Zeile auf der Rechnung als Element einer Datenmenge an. Deshalb sollte man sich auch in einem Computerprogramm nicht allzu weit von der menschlichen und somit auch der eigenen Denkweise entfernen. 8)

Also werde ich das mit JOIN machen und sehen, wo der nächste Haken ist.

Gruß
Hansa


hansa - Fr 06.12.02 13:45

Hi,

Zitat:
SelectSQL.Text := 'SELECT * FROM REC8 JOIN ART8 ON ART8.ID=ID_ART';


Da kriege ich zwar keine Fehlermeldung, aber die Artikel sind nicht da und es sind zu viele Rechnungspositionen zu sehen. Woher soll das Programm eigentlich wissen, daß es sich nur um eine Rechnung handeln soll ? Es sieht so aus, daß er alle Rechnungspositionen auflistet, die für diesen Kunden jemals erfaßt wurden. Die Einschränkung über die RecKopf-ID müßte ich ja auch noch unterbringen.

Moment, jetzt schreib ich mal die ganze Prozedur hier rein. Mit der SQL-Syntax bin ich noch nicht so fit :


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
procedure TReDruck.LabeledEdit1Exit(Sender: TObject);
begin
  RecDataMod.RecKopfDS.active := false;
  RecDataMod.KuDS.active := false;
  RecDataMod.RecDS.active := false;
  RecDataMod.ArtDS.active := false;
  WITH RecDataMod.RecKopfDS DO
    SelectSQL.Text := 'SELECT * FROM RECKOPF8 WHERE RECHNNR ='+LabeledEdit1.TEXT;
  RecDataMod.ReckopfDS.Active := true;

  IF NOT RecDataMod.ReckopfDS.IsEmpty THEN BEGIN

    WITH RecDataMod.KuDS DO
      SelectSQL.Text := 'SELECT * FROM KUNDE8 WHERE ID = ' +
      RecDataMod.RecKopfDS.FieldByName ('ID_KUNDE').AsString;
    RecDataMod.KuDS.active := true;

    WITH RecDataMod.RecDS DO
      SelectSQL.Text := 'SELECT * FROM ART8 JOIN REC8 ON ART8.ID=ID_ART';
    RecDataMod.RecDS.active := true;

    QuickRep1.Preview;
  END
  ELSE
    ShowMessage ('Eine Rechnung mit dieser Nr. existiert nicht !');
  Close;
end;


Glaube, daß das nicht mehr viel sein kann. Aber ich habe noch keine Vermutung, warum das nicht geht. 8)

Gruß
Hansa


LCS - Fr 06.12.02 13:59

Hi
Knapp daneben ist auch vorbei :mrgreen:. Als erstes brauchst du natürlich die Felder aus beiden Tabellen. Und dann muss das ganze natürlich noch auf die Rechnungsnummer eingeschränkt werden, die du brauchst.


Quelltext
1:
2:
3:
Select R.*, A.* FROM REC8 R 
JOIN ART8 A ON (A.ID_ART = R.ID_ART)
WHERE R.Rechnungsnummernfeld = Irgendeinerechnungsnummer


Gruss Lothar


hansa - Fr 06.12.02 14:18

Hi,


Quelltext
1:
2:
3:
4:
    WITH RecDataMod.RecDS DO
      SelectSQL.Text := 'Select R.*, A.* FROM REC8 R JOIN ART8 A ON (A.ID_ART = R.ID_ART)' +
      'WHERE R.ID_RECKOPF = RECKOPF8.ID';
    RecDataMod.RecDS.active := true;


Ich glaube, das hier ist noch knapper daneben. :mrgreen: Die .* sind wahrscheinlich überflüssig. Egal. Da kommt jetzt trotzdem die Feflermeldung : A.ID_ART Column unknown.

Gruß
Hansa


hansa - Fr 06.12.02 14:28

Hi,

das hier ist noch knapper daneben :


Quelltext
1:
2:
3:
4:
    WITH RecDataMod.RecDS DO
      SelectSQL.Text := 'Select R.*, A.* FROM REC8 R JOIN ART8 A ON (A.ID = R.ID_ART)' +
      'WHERE R.ID_RECKOPF = ' + RecDataMod.RecKopfDS.FieldByName ('ID').AsString;
    RecDataMod.RecDS.active := true;


Der Fehler war leicht zu finden, A.ID_ART gibt es wirklich nicht, das muß ganz einfach A.ID heißen. Nur, die Artikel sind nicht da. :bawling: Die 4 Rechnungspos. sind aber jetzt richtig.

Gruß
Hansa
[/quote]


LCS - Fr 06.12.02 14:28

Hi
Na, ja. Deine Spaltennamen kenn ich natürlich nicht. :roll: Ich bin einfach mal davon ausgegangen, dass die Artikel-ID in beiden Tabellen denselben Namen hat. Hier musst du natürlich dann selbst die entsprechenden Spaltennamen einsetzen.
Und so ganz nebenbei: Mit deiner Where-Klausel bin ich auch nicht ganz einverstanden:


Quelltext
1:
2:
3:
4:
5:
WITH RecDataMod.RecDS DO 
      SelectSQL.Text := 'Select R.*, A.* FROM REC8 R JOIN ART8 A ON (A.ID_ART = R.ID_ART)' + 
      'WHERE R.ID_RECKOPF = ' + 
      QuotedStr(RecDataMod.ReckopfDS.FieldByName('RECHNR').AsString); 
    RecDataMod.RecDS.active := true;


Versuchs mal so
Lothar


hansa - Fr 06.12.02 14:36

Hi,

so siehts im Moment aus :


Quelltext
1:
2:
3:
4:
5:
    WITH RecDataMod.RecDS DO
      SelectSQL.Text := 'Select R.*, A.* FROM REC8 R JOIN ART8 A ON (A.ID = R.ID_ART)' +
      'WHERE R.ID_RECKOPF = ' + RecDataMod.RecKopfDS.FieldByName ('ID').AsString;
    RecDataMod.RecDS.active := true;
    RecDataMod.ArtDS.active := true;


Das ArtDS war NICHT active = true, dafür kamen keine Artikel. Bei obigem kommt allerdings bei jeder Rechn.Pos. der erste Artikel in der Art-Table. Jetzt fliegt die Kugel ganz dicht an der Zielscheibe vorbei. Aufs Schützenfest kann ich aber noch nicht. :mrgreen:

Gruß
Hansa


LCS - Fr 06.12.02 14:48

Das versteh ich jetzt aber gar nicht. Der Status der ArtDs spielt an dieser Stelle überhaupt keine Rolle, weil die Artikel ja zusammen mit den Rechnungspositionen von RecDs geliefert werden. Hast du mal versucht die SQL direkt auszuführen (IBConsole)? Wenn es kein Problem mit den Datentypen von A.ID und R.ID_ART gibt muss das so eigentlich funktionieren.

Gruss Lothar


SvenL - Fr 06.12.02 15:24
Titel: Hi ,
sag mal hast DU in dem Datamod "RecDataMod" eventuel schon eine SQL Abfrage Drin
und solltest Du wärend der Laufzeit nicht diese Anweisung erstmal mit Clear löschen
und dann mit SQL.ADD Deine Abfrage übergeben???

Oder ist die Idee blöd?? :)


LCS - Fr 06.12.02 15:37
Titel: Re: Hi ,
Hi
SvenL hat folgendes geschrieben:
Oder ist die Idee blöd?? :)

Nein, ist es nicht. Und wenn wär's auch nicht schlimm. (Wir würden nur fürchterlich lästern und uns das Maul über dich zerreissen.) :mrgreen:
Es ist nur nicht notwendig weil er ja SelectSQL.Text verwendet und damit alles was schon drinsteht überschreibt.

Gruss Lothar


hansa - Sa 07.12.02 15:37

Hi,


Quelltext
1:
Select R.*,A.* FROM REC8 R JOIN ART8 A ON (A.ID = R.ID_ART) WHERE R.ID_RECKOPF = 50                    


hab ich jetzt mal als SQL-Script laufen lassen, kein Error. Nur wo sehe ich das Ergebnis ? 8) Jetzt aber noch eine Frage : Müssen die Felder, womit der JOIN abläuft eventuell unbedingt indiziert sein? Im Moment haben meine Tables nur einen primary Key auf den jeweiligen IDs liegen, sonst nichts! Bei meinem jetztigen Kenntnisstand ist noch durchaus damit zu rechnen, daß ich etwas elementares übersehe. :mrgreen:

Verwende ich obiges 1:1 in Delphi kriege ich jedenfalls folgendes : richtige Anzahl Rechn.Pos. mit richtigen Preisen, als Artikel kommt immer der erste Artikel, der in der Art-Table drin ist.

Gruß
Hansa


hansa - So 08.12.02 15:36

Hi,

das Problem hat sich etwas verlagert. 8) Da ich langsam überhaupt keine Idee mehr hatte, was da falsch sein könnte, bin ich einfach mal umgekehrt davon ausgegangen, daß es sehr wohl stimmt und der Fehler woanders steckt.

Ich muß dazu sagen, daß dieses Verhalten bei einem QuickReport auftritt. Für den habe ich noch kein Gespür entwickelt, da ich ihn erst diese Woche zum ersten mal benutze. :lol: Ich legte also ein Grid auf die Form und sieh an, alles war so wie es sein sollte. Vielleicht hat einer eine Idee, wo das Problem in Zusammenhang mit dem QR liegt.

Das mit dem Join geht also jetzt. Nun habe ich aber noch eine prinzipielle Frage. Also im Moment habe ich vier DataSets auf der Form. Für Rechnungskopf, Kunde, Rechnungspos., Artikel.

Die Datasource des Grids ist das Rechnungspos.-Dataset. Da stehen aber nun auch die Artikel mit drin. Wenn ich jedes Select-Statement als Datenmenge betrachte, wären es nur 3, trotz der 4 DataSets.

1. Select * From RecKopf
2. Select * from Kunde
3. Das mit dem Join, welches die Rechnungspos. und Artikel liefert.

Nun könnte ich ja zumindest noch 1 und 2 auch mit Join behandeln, dann hätte ich nur noch 2mal Select. Mir stellt sich aber jetzt die Frage, was ist eigentlich besser ? Also ehrlich gesagt bin ich fast der Meinung, die SQL-Abfragen nicht zu sehr zu verschachteln, damit man den Überblick behält. Aber ich weiß nicht, ob das aus anderen Gründen vielleicht nicht so ratsam ist.

Gruß
Hansa


hansa - So 08.12.02 16:05

Hi,

die Artikel sind jetzt auch im Quickreport. Allerdings muß man den Artikel-Feldern jetzt als DataSet RechnPos zuordnen. :hair: Bei meinem letzten Posting habe ich so was in der Richtung schon geahnt. In einem größeren Team könnte das tödlich sein. Muß man sich an solche Sachen gewöhnen oder bin ich auf dem Holzweg und mache irgendwas verkehrt ?

Gruß
Hansa