*jiriki*: SQL Abfrage über drei Tabellen

Moin,

Folgendes Szenario:

Es gibt drei Tabellen ( kunden, vertrag, rechnung ), wobei der PrimaryKey bei der Kunden-Tabelle die "kunden_nr" ist, bei der Vertrag-Tabelle die "vertrag_nr" und bei der Rechnung-Tabelle die "rechnungs_nr".

Die Rechnung-Tabelle hat sowohl einen Eintrag für die zugehörige kunden_nr als auch die vertrags_nr. Die Vertrag-Tabelle hat nur einen Eintrag für die zugehörige kunden_nr, und die Kunden-Tabelle besitzt keine Einträge auf externe PrimaryKeys.

Ich hab mir jetzt in einer ersten Abfrage die Rechnung mit dem neuesten Eintragungsdatum je Vertragsnummer geholt, d.h. folgende Abfrage:

CREATE TEMPORARY TABLE
tmp
SELECT
kunden_nr, vertrags_nr, MAX( eintragung ) as last
FROM
rechnung
GROUP BY vertrags_nr;

Hier kommt das gewünschte Ergebnis raus.

Nun möchte ich in einer zweiten Abfrage die Datensätze in der Temporären Tabelle mit den zugehörigen Werten aus den drei Tabellen kunden, vertrag und rechnung auffüllen.

Ich bin dazu zur Zeit bei folgender Abfrage, die mir aber aber aus 3 usprünglichen Datensätzen in der Temporären Tabelle 5 Datensätze macht:

SELECT
s.firma, s.vorname, s.name, s.strasse, s.nr, s.plz, s.ort, s.email,
v.preis, v.domain, v.leistung, v.intervall, v.zahlungsart, v.bank,
r.kunden_nr, r.vertrags_nr, r.rechnungs_nr, r.abrechnung
FROM
tmp, vertrag as v, rechnung as r, stamm as s
WHERE
tmp.vertrags_nr = v.vertrags_nr
AND
tmp.kunden_nr = s.kunden_nr
AND
tmp.last = r.abrechnung;

Bin für jede Hilfe dankbar. Gruß, *jiriki*

  1. Hi,

    SELECT
    s.firma, s.vorname, s.name, s.strasse, s.nr, s.plz, s.ort, s.email,
    v.preis, v.domain, v.leistung, v.intervall, v.zahlungsart, v.bank,
    r.kunden_nr, r.vertrags_nr, r.rechnungs_nr, r.abrechnung
    FROM
    tmp, vertrag as v, rechnung as r, stamm as s
    WHERE
    tmp.vertrags_nr = v.vertrags_nr
    AND
    tmp.kunden_nr = s.kunden_nr
    AND
    tmp.last = r.abrechnung;

    Soweit ich weiss, musst du das per JOIN machen. Wobei zu überlegen ist, ob nicht einzelne Abfrage schneller ablaufen, weil JOINs ziemlich ressourcen fressen.
    Aber das musst du selber ausprobieren ;)

    1. Hi,

      SELECT
      s.firma, s.vorname, s.name, s.strasse, s.nr, s.plz, s.ort, s.email,
      v.preis, v.domain, v.leistung, v.intervall, v.zahlungsart, v.bank,
      r.kunden_nr, r.vertrags_nr, r.rechnungs_nr, r.abrechnung
      FROM
      tmp, vertrag as v, rechnung as r, stamm as s
      WHERE
      tmp.vertrags_nr = v.vertrags_nr
      AND
      tmp.kunden_nr = s.kunden_nr
      AND
      tmp.last = r.abrechnung;

      Soweit ich weiss, musst du das per JOIN machen. Wobei zu überlegen ist, ob nicht einzelne Abfrage schneller ablaufen, weil JOINs ziemlich ressourcen fressen.

      Er _macht_ einen join!
      Auch wenn das Wort dort nicht drin steht.

      Gruß
      Reiner

      1. Hi,

        Er _macht_ einen join!
        Auch wenn das Wort dort nicht drin steht.

        Muss das jetzt nicht mehr drinstehen? Ich glaub in MySQL3 ging das noch nicht ohne, kann das sein?
        Da ich meine Datenbankstrukturen immer selber entwerfe, brauche ich keine JIONs in meiner Software ;) Deshalb bin ich mit denen nicht so bewandert.

        1. Hi,

          Er _macht_ einen join!
          Auch wenn das Wort dort nicht drin steht.

          Muss das jetzt nicht mehr drinstehen? Ich glaub in MySQL3 ging das noch nicht ohne, kann das sein?

          Nee, die Bedingungen "where tab_a.feld = tab_b.feld" wirkt auch wie ein join.

          Gruß
          Reiner

  2. n'abend,

    FROM
    tmp, vertrag as v, rechnung as r, stamm as s

    ..

    AND
    tmp.last = r.abrechnung;

    die rechnungstabelle mit dem datum statt mit der rechnungsnummer zu verknüpfen ist vermutlich das problem.
    (ist stamm nun deine kundentabelle?)

    Grüße,
    Robert

    1. n'abend,

      FROM
      tmp, vertrag as v, rechnung as r, stamm as s
      ..
      AND
      tmp.last = r.abrechnung;

      die rechnungstabelle mit dem datum statt mit der rechnungsnummer zu verknüpfen ist vermutlich das problem.
      (ist stamm nun deine kundentabelle?)

      Ja. Stimmt, jetzt seh ichs auch. Das Abrechnungsdatum ist bei zwei Kunden gleich, deswegen gibts da für jede Rechnung jeweils zwei Datensätze und macht 5 statt 3.

      Stamm ist die kundentabelle, sorry.

      [nach einem kurzen Besuch im lokalen phpMyAdmin...]
      Also ich habs jetzt so gelöst, dass ich mir in der ersten Abfrage zusätzlich die MAX( rechnungs_nr ) hole, was in diesem Fall passt, weil der Rechnungs-Datensatz mit dem neuesten Datum auch immer die höchste Rechnungsnummer hat.
      Hast du eine Idee, wie ich mir die zum jeweiligen neuesten Datum gehörende Rechnungsnummer holen könnte, wenn die Rechnungsnummer bel. variieren würde?

      Gruß und danke! *jiriki*

  3. Hallo,

    Dein Problem scheint mir analog zu dem in diesem Archivthread zu sein.

    Du versuchst Dich an der von mir (dort mit einem View statt der temporären Tabelle) vorgestellten Lösung, bei geeigneter Unterstützung durch das DBMS, das Du uns geflissentlich verschweigst, würde ich Iljas Lösung bevorzugen.

    Die Tatsache, dass es bei Dir drei Tabellen gibt, ändert das Problem nicht grundlegend, ein JOIN oder zwei mehr.

    Dein SQL-Code ist fehlerhaft:

      
    
    > CREATE TEMPORARY TABLE  
    > tmp  
    > SELECT  
    > kunden_nr, vertrags_nr, MAX( eintragung ) as last  
    > FROM  
    > rechnung  
    > GROUP BY vertrags_nr;  
    
    

    es fehlt die Gruppierung nach kunden_nr.

    Hier kommt das gewünschte Ergebnis raus.

    Das erhöht die Wahrscheinlichkeit, dass Du MySQL verwendest. Ich kenne halt kein anderes DBMS, das diesen fehlerhaften Code schluckt.

    Nun möchte ich in einer zweiten Abfrage die Datensätze in der Temporären Tabelle mit den zugehörigen Werten aus den drei Tabellen kunden, vertrag und rechnung auffüllen.

    Wenn ich Dich richtig verstanden habe, dann hättest Du gern zu jedem Vertrag die neueste Rechnung mit Rechnungsdetails, ein paar Vertragsdetails und ein paar Kundendetails. Richtig?

      
    SELECT  
        s.firma,  
        s.kunden_nr,  
        /* weitere Details aus s */  
        v.preis,  
        /* weitere Detais aus v */  
        r.kunden_nr,  
        r.vertrags_nr,  
        r.rechnungs_nr,  
        r.abrechnung  
    FROM  
        rechnung as r  
    INNER JOIN vertrag as v ON r.vertrags_nr = v.vertrags_nr  
    INNER JOIN stamm as s ON v.kunden_nr = s.kunden_nr  
    
    

    liefert Dir alle Details zu allen Rechnungen aller Verträge aller Kunden. Auf diese Ausgangsbasis kannst Du analog die Lösungen in oben angeführtem Thread anwenden.

    PS: Die Spalte kunden_nr in der Tabelle rechnung ist redundant. Deswegen solltest Du diese Spalte dort weglassen. Selbstverständlich musst Du in diesem Fall bei Deiner Abfrage die kunden_nr aus der Tabelle vertrag auslesen.

    Freundliche Grüße

    Vinzenz

    1. Hallo,

      Dein Problem scheint mir analog zu dem in diesem Archivthread zu sein.

      Du versuchst Dich an der von mir (dort mit einem View statt der temporären Tabelle) vorgestellten Lösung, bei geeigneter Unterstützung durch das DBMS, das Du uns geflissentlich verschweigst, würde ich Iljas Lösung bevorzugen.

      Die Tatsache, dass es bei Dir drei Tabellen gibt, ändert das Problem nicht grundlegend, ein JOIN oder zwei mehr.

      Dein SQL-Code ist fehlerhaft:

      CREATE TEMPORARY TABLE
      tmp
      SELECT
      kunden_nr, vertrags_nr, MAX( eintragung ) as last
      FROM
      rechnung
      GROUP BY vertrags_nr;

      
      > es fehlt die Gruppierung nach kunden\_nr.  
      
      Weil das ne SQL-Vorgabe ist? Notwendig ists nicht. Kann man nach zwei Spalten gruppieren?  
      
      >   
      > > Hier kommt das gewünschte Ergebnis raus.  
      >   
      > Das erhöht die Wahrscheinlichkeit, dass Du MySQL verwendest. Ich kenne halt kein anderes DBMS, das diesen fehlerhaften Code schluckt.  
        
      Stimmt. MySQL 4.0.20a (debug) um genau zu sein.  
      
      >   
      > > Nun möchte ich in einer zweiten Abfrage die Datensätze in der Temporären Tabelle mit den zugehörigen Werten aus den drei Tabellen kunden, vertrag und rechnung auffüllen.  
      >   
      > Wenn ich Dich richtig verstanden habe, dann hättest Du gern zu jedem Vertrag die neueste Rechnung mit Rechnungsdetails, ein paar Vertragsdetails und ein paar Kundendetails. Richtig?  
      >   
      > ~~~sql
        
      
      > SELECT  
      >     s.firma,  
      >     s.kunden_nr,  
      >     /* weitere Details aus s */  
      >     v.preis,  
      >     /* weitere Detais aus v */  
      >     r.kunden_nr,  
      >     r.vertrags_nr,  
      >     r.rechnungs_nr,  
      >     r.abrechnung  
      > FROM  
      >     rechnung as r  
      > INNER JOIN vertrag as v ON r.vertrags_nr = v.vertrags_nr  
      > INNER JOIN stamm as s ON v.kunden_nr = s.kunden_nr  
      > 
      
      

      liefert Dir alle Details zu allen Rechnungen aller Verträge aller Kunden. Auf diese Ausgangsbasis kannst Du analog die Lösungen in oben angeführtem Thread anwenden.

      Klappt perfekt. Danke für den Tip. Wie würde ich hier jetzt noch die uspr., aber beim ersten Beitrag weggelassene WHERE-Bedingung unterbringen:

      date_add( last, INTERVAL intervall MONTH ) <= NOW();

      da ich nur die Rechnungen rausgreifen möchte, deren Rechnungsdatum plus eine bestimmte Zeit "intervall" dem aktuellen Datuzm entspricht oder unterschreitet?

      PS: Die Spalte kunden_nr in der Tabelle rechnung ist redundant. Deswegen solltest Du diese Spalte dort weglassen. Selbstverständlich musst Du in diesem Fall bei Deiner Abfrage die kunden_nr aus der Tabelle vertrag auslesen.

      Ja, das war mir bewusst, hab ich aber aus Bequemlichkeit in den SQL-Anfragen in Kauf genommen, weils die einzige Redundanz ist und der DB-Umfang gering ist.

      Freundliche Grüße

      Vinzenz

      Danke! *jiriki*