Ankündigung

Einklappen
Keine Ankündigung bisher.

Datenbank Probleme: Wareneingänge / Warenausgänge -> Bestand

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Datenbank Probleme: Wareneingänge / Warenausgänge -> Bestand

    Hallo zusammen,

    ich bin durch die aktuelle Kurzarbeitsphase, auf die Idee gekommen mich wieder mehr mit meinem Hobby aus der Schulzeit zu befassen, bin daher Laie und Anfänger was Datenbanken angeht.

    Ich wollte ein kleines Lagertool programmieren:
    1. Wareneingänge und Warenausgänge erfassen
    2. Übersicht der Ein-und Ausgänge
    3. Als Ergebnis eine Bestandsübersicht

    1. und 2. haben soweit auch funktioniert.


    Meine Problem zu 3. sind:

    1. dass es Ausgänge geben kann, zu denen es keinen Eingang gab.
    Meine Abfrage sucht in der Tabelle Wareneingang und Warenausgang nach gleichen Artikelnummern um den Bestand zu errechnen -> klappt dann nicht, und ich finde keinen Ansatz zur Lösung.

    Aktuell sieht meine Abfrage so aus:

    Code:
    $stmt = $mysql->prepare("SELECT we.EAN, we.Artikelname, we.VPEwe, we.Mengewe, wa.EAN, wa.Artikelname, 
    wa.VPEwa, wa.Mengewa, sum(we.Mengewe)-sum(wa.Mengewa) AS Bestand From wareneingang we LEFT JOIN 
    warenausgang wa ON we.EAN = wa.EAN GROUP BY we.EAN");
    Wenn es nicht zu jedem Eingang einen passenden Ausgang gibt, geht der Datensatz also verloren. Hab ich einen Denkfehler oder bin ich mit meinem generellen Aufbau schon falsch gefahren?

    2. Zusätzlich habe ich Ausgangsartikelnummern, die aus quasi von zwei Eingangsnummern beliefert werden, hier habe ich leider nicht mal einen Ansatz...

    Für Tipps oder einen Denkanstoß in die richtige Richtung bin ich sehr dankbar.

    VG erasn

  • #2
    Um das nachvollziehen zu können und zu sagen ob es der richtige Weg ist, solltest unbedingt deine DB Struktur zeigen als CREATE Statement mit einigen Beispieldatensätzen. Kein Bild oder Link bitte sondern Text den man kopieren kann.
    So kann man nur raten aufgrund mangelnder Basiskenntnisse.

    Alternativ kannst du auch ein sqlfiddle erstellen.

    Kommentar


    • #3
      Rein aus sicht von SQL suchst du ein FULL OUTER JOIN. Diese JOIN art untersützt Mysql aber nicht. In Mysql musst du ein LEFT JOIN machen und ein RIGHT JOIN und das mit UNION verknüpfen. Bei Google findest du mit "mysql full outer join" genügend Material. Um damit dann Summen zu berechnen musst du das ganze noch in einem Subquery stecken. Die Alternative ist Wareneingänge und Warenausgänge getrennt zu Zählen und auch wieder in einem Subquery zu Summieren.

      Designtechnisch kannst du dem ganzen aus dem Weg gehen, wenn du eine Tabelle mit "Stammdaten" anlegst. Dann kannst du "stammdaten LEFT JOIN we ON () LEFT JOIN wa ON ()" machen. Vom Gefühl her ist das bei dem Use Case auch nicht soweit weg.
      Was du dir auch noch überlegen kannst, brauchst du die Unterscheidung Wareneingang und Warenausgang über getrennte Tabellen? Das sind alles Bestandsbewegungen. Hast du Anforderungen die zwei Tabellen erfordern?

      Generell zu deinem Ansatz, den Bestand aus den Bewegungen zu berechnen. Das ist eine Lösung die nicht vernünftig skaliert. Wenn du den Bestand oft brauchst, oder du ein paar mehr Bewegungen hast, kann es sinn machen den Bestand explizit zu speichern. Z.B. eine Tabelle "bestand" mit den Feldern "ean" und "menge". Im kleinen Maßstab ist dein Ansatz aber noch kein Problem. Da kannst du dein Query, der die Bestände berechnet, in ein View packen und hast im Prinzip das selbe erreicht. Das wird dann zu einem Problem wenn du 1000te Buchungen am Tag hast.

      Kommentar


      • #4
        Vielen Dank für eure Antworten.

        Ich habe tatsächlich auch die dritte Tabelle Stammdaten. mit den zwei LEFT JOIN Anweisungen klappt es auch soweit, da bin ich einfach nicht drauf gekommen.

        Meine Idee hinter den getrennten Wareneingang und Warenausgang Tabellen war, dass ich die separaten Übersichten für die Eingänge und Ausgänge leichter auslesen lassen kann.

        Da das Auslesen nun funktioniert ergibt sich jedoch ein neues Problem: ich müsste nun noch sagen können, "suche explizit nach Art-Nr. 1, 2 und 3 und rechne mir davon die Summe aus" da fehlt mir wieder irgendwo was.
        Und danach folgt theoretisch das nächste Problem, die Elemente die bei einem Warenausgang unter Packungszugabe und Verpackungset mit eingegeben werden möchte ich eigentlich auch verwalten.

        Habt ihr dazu vielleicht auch noch einen Denkanstoß für mich?



        Generell sind meine drei Tabellen folgend aufgebaut: (hoffe es entspricht der Anforderung des kopierbaren Textes?)


        Stammdaten:
        Code:
        CREATE TABLE `stammdaten` (
        `EAN` varchar(255) COLLATE utf8_bin NOT NULL,
        `Artikelname` varchar(255) COLLATE utf8_bin DEFAULT NULL,
        `VPE` int(11) DEFAULT NULL,
        `Packungsart` varchar(255) COLLATE utf8_bin DEFAULT NULL,
        `varianteweiß` int(11) DEFAULT NULL,
        `packungszugabe` int(11) DEFAULT NULL,
        `verpackungset` int(11) DEFAULT NULL,
        `varianteschwarz` int(11) DEFAULT NULL,
        `eupal` int(11) DEFAULT NULL,
        PRIMARY KEY (`EAN`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
        Wareneingang:
        Code:
        CREATE TABLE `wareneingang` (
        `Datum` date NOT NULL,
        `Lieferant` tinytext COLLATE utf8_bin NOT NULL,
        `Artikelname` tinytext COLLATE utf8_bin NOT NULL,
        `EAN` varchar(255) COLLATE utf8_bin NOT NULL,
        `Mengewe` int(11) NOT NULL,
        `VPEwe` int(11) DEFAULT NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
        Warenausgang:
        Code:
        CREATE TABLE `warenausgang` (
        `Datum` date NOT NULL,
        `Kunde` tinytext COLLATE utf8_bin NOT NULL,
        `PLZ` int(11) NOT NULL,
        `Ort` tinytext COLLATE utf8_bin NOT NULL,
        `Strasse` tinytext COLLATE utf8_bin NOT NULL,
        `Artikelname` tinytext COLLATE utf8_bin NOT NULL,
        `EANwa` varchar(255) COLLATE utf8_bin NOT NULL,
        `Mengewa` int(255) NOT NULL,
        `packungszugabe` int(11) DEFAULT NULL,
        `verpackungset` int(11) DEFAULT NULL,
        `eupal` int(11) DEFAULT NULL,
        `VPEwa` int(11) DEFAULT NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

        Viele Grüße

        Kommentar


        • #5
          Zu dem ersten Problem. Du hängst an den Query ein WHERE an.

          PHP-Code:
          WHERE stammdaten.ean IN ('ean1''ean2''ean3', ...) 
          Bei dem zweiten Problem müsstest du erstmal erklären was ein Packungszugabe, Verpackungset ist. In deinem Datenbankschema sind das Ints, das können damit keine Produkte sein.

          Kommentar


          • #6
            Randnotiz:
            SELECT we.EAN, we.Artikelname, ..., wa.EAN, wa.Artikelname,
            Hier musst du Aliase einsetzen, sonst überschreiben sich die Informationen aus 'we' in einem assoziativen Array.
            Über 90% aller Gewaltverbrechen passieren innerhalb von 24 Stunden nach dem Konsum von Brot.

            Kommentar


            • #7
              Danke, so habe ich es versucht, bisher ohne Erfolg, dann schaue ich mir es nochmal genauer an, wenn ich da schon in die richtige Richtung unterwegs war.

              Zitat von erc Beitrag anzeigen
              Zu dem ersten Problem. Du hängst an den Query ein WHERE an.

              PHP-Code:
              WHERE stammdaten.ean IN ('ean1''ean2''ean3', ...) 
              Bei dem zweiten Problem müsstest du erstmal erklären was ein Packungszugabe, Verpackungset ist. In deinem Datenbankschema sind das Ints, das können damit keine Produkte sein.
              Sind beides Zahlenwerte; Verpackungset steht dabei nur für die Verpackung, ich habe Artikelnummern die unverpackt sind und verpackte, es gibt jedoch nur eine Verpackung daher wollte ich es über diese Eingabe direkt mit "abführen".
              Die Zugabe ist quasi damit identisch, wenn es um die verpackte Version geht, gibt es auch immer die gleiche Zugabe mit in die Packung, also auch als reiner Zahlenwert.

              Hier musst du Aliase einsetzen, sonst überschreiben sich die Informationen aus 'we' in einem assoziativen Array.
              Ich lasse die Ergebnisse über:

              PHP-Code:
              $stmt->execute();
                         
              $res $stmt->fetchALL();
                         foreach(
              $res as $row){
                         echo 
              "<tr><td>" $row['sEAN']."</td>... 
              ausgeben, müsste ich damit doch entgegen wirken, oder?

              Kommentar


              • #8
                echo "<tr><td>" . $row['sEAN']."</td>...
                sEAN? Sehe ich ehrlich gesagt gar nicht im Query... geht es immer noch um das Query aus dem Eingangspost?

                Aka:
                SELECT we.EAN, we.Artikelname, we.VPEwe, we.Mengewe, wa.EAN, wa.Artikelname, wa.VPEwa, wa.Mengewa, sum(we.Mengewe)-sum(wa.Mengewa) AS Bestand
                Über 90% aller Gewaltverbrechen passieren innerhalb von 24 Stunden nach dem Konsum von Brot.

                Kommentar


                • #9
                  Oh, ich habe die Abfrage nach dem Tipp erc angepasst. Hätte ich vielleicht mit in den Post nehmen sollen...
                  Und im Echo Statement habe ich dann für jede abgerufene Spalte die Ausgabe.

                  PHP-Code:
                  $stmt $mysql->prepare("SELECT s.sEAN, s.sArtikelname, s.sVPE, we.EAN, we.Artikelname, we.VPEwe, we.Mengewe, wa.EAN, wa.Artikelname, wa.VPEwa, wa.Mengewa FROM stammdaten s LEFT JOIN wareneingang we ON we.EAN = s.sEAN LEFT JOIN warenausgang wa ON wa.EAN = s.sEAN GROUP BY s.sEAN" ); 
                  Die Abfrage funktioniert zum Glück auch. Auch die Beschränkung der Abfrage auf die Artikelnummern mit der Ergänzung WHERE klappt.
                  PHP-Code:
                  SELECT s.sEANs.sArtikelnames.sVPEwe.EANwe.Artikelnamewe.VPEwewe.Mengewewa.EANwa.Artikelnamewa.VPEwawa.Mengewa FROM stammdaten s LEFT JOIN wareneingang we ON we.EAN s.sEAN LEFT JOIN warenausgang wa ON wa.EAN s.sEAN WHERE s.sEAN IN ('100001','100002','100003')" ); 
                  Habe ich eine Chance diese drei Zeilen in einer ausgeben zulassen, GROUP BY bringt mich hier nicht an Ziel, da Nummer und Name ja unterschiedlich ist, könnte nur den Umweg einer zusätzlichen Spalte in der Datenbank gehen, die ich dann als "Produktkategorie" festlege und darüber Gruppieren lasse?

                  Kommentar


                  • #10
                    geht prinzipiell, Demo:

                    Code:
                    edb=> create table erasn(i int);
                    CREATE TABLE
                    edb=*> insert into erasn select * from generate_series(1,10) s;
                    INSERT 0 10
                    edb=*> select * from erasn ;
                     i  
                    ----
                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                     10
                    (10 rows)
                    
                    edb=*> select sum(i) filter (where i = 3) as s3, sum(i) filter(where i = 4) as s4, sum(i) filter (where i = 7) as s7 from erasn ;
                     s3 | s4 | s7
                    ----+----+----
                      3 |  4 |  7
                    (1 row)
                    Du siehst den Weg?
                    PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

                    Kommentar


                    • #11
                      übrigens: Deine Tabellen in #4 sind gruselig. Aber damit mußt Du ja leben, nicht ich...
                      PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

                      Kommentar


                      • #12
                        Zitat von erasn Beitrag anzeigen
                        Habe ich eine Chance diese drei Zeilen in einer ausgeben zulassen, GROUP BY bringt mich hier nicht an Ziel, da Nummer und Name ja unterschiedlich ist
                        Du lässt Nummer, Name etc weg und setzt die Spalten jeweils in SUM(). Eine Aggregat-Funktion (also das SUM) ohne GROUP BY gruppiert dir alle Datensätze der Abfrage.

                        Kommentar


                        • #13
                          Danke für alle Tipps und Hinweise, bisher klappt nun alles wie gedacht.

                          übrigens: Deine Tabellen in #4 sind gruselig. Aber damit mußt Du ja leben, nicht ich...
                          Ich habe nie behauptet, dass die schön sind, wie oben erwähnt bin ich kompletter Anfänger und versuche mir für meine kleinen Anwendungsfälle die Mechanismen klar zu machen.
                          Daher immer offen für Kritik. (:

                          Kommentar


                          • #14
                            Tabelle stammdaten:
                            • ean als varchar, schlecht. In PG gäbe es dafür einen passenden Datentyp, der u.a. auch die ean validiert, und weniger Speicher belegt.
                            • die anderen Felder sind schwer zu beurteilen, da ich deren Bedeutung etc. nicht kenne. Aber Packungsarten gibt es sicher vordefinierte, und daher nicht als Freitextfeld


                            Wareneingang:
                            • kein Foreign Key constraint EAN auf stammdaten
                            • redundant der Artikelname
                            • kein PK


                            Warenausgang:
                            • PLZ als int, Dresden beginnt mit 0, viel Spaß
                            • wieder redundant der Artikelname
                            • wieder kein FK zu Stammdaten
                            • kein PK
                            PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

                            Kommentar

                            Lädt...
                            X