Jörg: Daten eintragen (Lösungsansatz)

Hallo,

ich möchte in eine Tabelle Monate ab einem bestimmten Zeitpunkt eintragen. Ich möchte aber, dass ich niemals den nächsten Monat vor dem 20. des laufenden Monats eintrage. zudem soll der Prozess jederzeit gestartet, pausiert und ggf. nach Monaten fortgesetzt werden können. Im Ergebnis soll eine Aufzählung der Monate ab Startzeitpunkt und ohne Doppler bis "heute" sein. Wobei "heute" entwerder den aktuellen Monat bedeutet oder den nächsten Monat (falls wir schon über dem 20. sind).

Habe ich etwas übersehen bei meinem Ansatz? (das Jahr speichere ich natürlich mit, habe es aber jetzt mal hier heraus gelassen)

$thisMonth = new DateTime('this month');
$heutigerTag = $thisMonth->format('d');
//echo $heutigerTag;

if ($heutigerTag > 19) {
    $nextMonth = new DateTime('next month');
} else {
    $nextMonth = new DateTime('this month');
}

$Monat = $nextMonth->format('m');
$Jahr = $nextMonth->format('Y');


for($i = 1;$i <= $Monat;$i++) {
// Pseudocode
wenn über Startmonat, dann Eintrag
}
  1. Hallo Jörg,

    lass uns erstmal über die Fachlichkeit reden.

    ich möchte in eine Tabelle Monate ab einem bestimmten Zeitpunkt eintragen.
    Im Ergebnis soll eine Aufzählung der Monate ab Startzeitpunkt und ohne Doppler bis "heute" sein.

    D.h. die Monate in der Tabelle befinden sich in der Vergangenheit. Wenn der Startzeitpunkt der März wäre, würde man heute (01.07.) die Monate März bis Juni sehen, und in 3 Wochen (22.07.) die Monate März bis Juli?

    Zu deinem Code: Der Startzeitpunkt ist nur indirekt erkennbar. Deine Schleife beginnt bei 1, d.h. der Startmonat in deinem Code ist der Januar?!

    // wenn über Startmonat, dann Eintrag

    Das ist dann wieder seltsam, denn in $Monat steht ja das Ergebnis der Rechnerei mit this/next month, also eigentlich eine Variante von "heute".

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      D.h. die Monate in der Tabelle befinden sich in der Vergangenheit. Wenn der Startzeitpunkt der März wäre, würde man heute (01.07.) die Monate März bis Juni sehen, und in 3 Wochen (22.07.) die Monate März bis Juli?

      Genau.

      Zu deinem Code: Der Startzeitpunkt ist nur indirekt erkennbar. Deine Schleife beginnt bei 1, d.h. der Startmonat in deinem Code ist der Januar?!

      Eigentlich gar nicht erkennbar, hätte ich gedacht. Schleife bei 1 ist korrekt, das Moatsarray hat hier den Januar.

      // wenn über Startmonat, dann Eintrag

      Das ist dann wieder seltsam, denn in $Monat steht ja das Ergebnis der Rechnerei mit this/next month, also eigentlich eine Variante von "heute".

      Oh, das wird grad zu kompliziert, ich liefere mal Code nach.

      $thisMonth = new DateTime('this month');
      $heutigerTag = $thisMonth->format('d');
      echo $heutigerTag;
      
      if ($heutigerTag > 19) {
          $nextMonth = new DateTime('next month');
      } else {
          $nextMonth = new DateTime('this month');
      }
      
      $Monat = $nextMonth->format('m');
      $Jahr = $nextMonth->format('Y');
      
      $arr_monate=array('0','Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember');
      
      
          $query_alle = "SELECT
          ID,
          KundenID,
          Ab_Monat,
          Ab_Jahr
          FROM table
          ";
          $result_alle = mysqli_query($db,$query_alle);
          if($result_alle == FALSE) {
              error($query_alle);
          }
      
          while($row_alle = mysqli_fetch_row($result_alle)) {
          for($i = 1;$i <= $Monat;$i++) {
              if ($i >= $row_alle[2] && $Jahr >= $row_alle[3]) {
             //Insert
      ...
      }
      }
      }
      
      

      Dieses Codeszenario funktioniert bei meinen Tests bisher. Aber wie das bei Tests so ist, man weiß nie so recht, ob man alle in der Praxis denkbaren Szenarien damit auch wirklich erfasst hat.

      Jörg

      1. Hallo Jörg,

        okay, ich hatte dein Ziel komplett mistverstanden. Danke an Mudgard für die Aufklärung.

        Da ist noch ein WHILE drumherum. D.h. Du machst das vermutlich pro Kunde.

        Den Zweck von "table" habe ich noch nicht verstanden. Ist das eine Zusatztabelle, wo pro Kunde genau ein Satz drinsteht, und den aktualisierst Du am Ende mit dem zuletzt geINSERTeten Datum? Oder ist "table" die gleiche Tabelle, in die Du auch die INSERTs machst? Das hast Du nicht verraten.

        By The Way: Mit fetch_assoc würdest Du ein $row-Array mit Spaltennamen bekommen und müsstest nicht mit Spaltenindexen herumhantieren...

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf aka McGuyver, 😉

          Da ist noch ein WHILE drumherum. D.h. Du machst das vermutlich pro Kunde.

          Nicht pro Kunde, sondern pro Eintrag meiner Tabelle, in der alle monatlich zu berechnenden Vorgänge enthalten sind. Das können auch mehrere je Kunde sein.

          Den Zweck von "table" habe ich noch nicht verstanden. Ist das eine Zusatztabelle, wo pro Kunde genau ein Satz drinsteht, und den aktualisierst Du am Ende mit dem zuletzt geINSERTeten Datum? Oder ist "table" die gleiche Tabelle, in die Du auch die INSERTs machst? Das hast Du nicht verraten.

          Das ist die Zusatztabelle, von der ich weiter oben sprach. Die, in der alle monatlich zu berechnenden Vorgänge enthalten sind.

          By The Way: Mit fetch_assoc würdest Du ein $row-Array mit Spaltennamen bekommen und müsstest nicht mit Spaltenindexen herumhantieren...

          Mit fetch_array würde ich sogar Beides bekommen. Aber ich habe mich im Verlkaufe der Jahre so an die Spaltenindexe gewöhnt, dass jeglicher Versuch gescheitert ist, auf Spaltennamen umzusteigen. In sowas kann ich wirklich hoffnungslos "beständig" sein. 😇

          Aber alles in allem ist meine Ausgangsfrage noch nicht wirklich beantwortet.,

          Fällt irgendwem eine Fehlerquelle ein, die ich unberücksichtigt gelassen habe?

          Jörg

          1. Hallo Jörg,

            Fällt irgendwem eine Fehlerquelle ein, die ich unberücksichtigt gelassen habe?

            Solange ich nicht verstanden habe, was Du eigentlich genau tust und welche Randbedingungen gelten, kann man das kaum beantworten.

            Das ist die Zusatztabelle, von der ich weiter oben sprach.

            Naja, das Forum zeigt es "weiter unten" 😉

            Die, in der alle monatlich zu berechnenden Vorgänge enthalten sind.

            Was habe ich mir darunter vorzustellen? Ich versuche mal, deine Arbeit zu tun und mache ein ausführliches Beispiel. Natürlich kann ich mich dabei völlig verhauen, aber dann haben wir zumindest eine Grundlage auf der wir weiter klären können, was du tust und was Du willst (was ja bekanntlich nicht das Gleiche sein muss 😂).

            Aus deinem SELECT sehe ich, dass es da eine ID gibt, eine KundenID, ein Ab-Jahr und einen Ab-Monat. In deinem anderen Beitrag stand noch was von mehreren Flags.

            Die Tabelle könnte also eine Zeile enthalten, dass für den Kunden 4711 ab 03/2020 ein Vorgang auszuführen ist. Welcher Art dieser Vorgang ist, dürfte aus weiteren Spalten hervorgehen, die Du nicht gezeigt hast. Mutmaßlich könnte es für einen Kunden auch mehrere monatlich zu berechnenden Vorgänge geben. Aber egal, das ist ein Detail...

            Wenn der Kunde 4711 neu ins System kommt und März '20 die erste Rechnung bekommt, müsste in deiner Table also ein Satz für ab_jahr 2020 und ab_monat 3 geschrieben werden. Wenn ich deine Idee richtig verstehe, ist das der Keim für dein Verfahren. Wenn Ende März 2020 der Cron-Job Läuft, findet er den Satz für März '20 und probiert nun Sätze für Jan, Feb und März 2020 zu schreiben (du beginnst ja bei 1 zu zählen). Jan und Feb werden durch deinen IF abgefangen. Im März geht es schief, weil der Satz schon da ist. Und für den April gelingt der Insert.

            Nun läuft die WHILE-Schleife in die nächste Runde. Sie findet den April '20 vermutlich nicht, weil der SELECT sein Ergebnis vorher geladen hat. Also ist Ende für diesen Kunden.

            Soweit, so gut. Betrachten wir den nächsten Cron-Job, Ende April. Der findet den März 20. Die Schleife läuft los, Jan - nö. Feb - nö. März - dup. April - dup. Mai - Erfolg. Als nächstes findet er den April 20. Und es geht von vorn los. Jan - nö. Feb - nö. März - dup. April - dup. Mai - dup. Das war vergebliche Liebesmühe.

            Deswegen meinte Mudgard, du müsstest den jeweils jüngsten schon vorhandenen Satz finden, und ab diesem loslaufen.

            Betrachten wir einen anderen Fall. Die Tabelle enthält für Kunde 4711 genau einen Satz mit ab_jahr=2020 und ab_monat=12. Der nächste Cron-Job Ende Dezember startet. $Jahr ist bei Dir nun 2021 und $Monat ist 1.

            Du machst den SELECT und findest Kunde 4711, Ab_Monat=12, Ab_Jahr=2020. Deine Schleife läuft los, mit $i=1.

            Im ersten Durchlauf wird 1 >= 12 && 2021 >= 2020 geprüft. Das ist FALSE.
            Und nun ist die Schleife zu Ende, weil $i <= $Monat nicht mehr erfüllt ist.

            Jahresübergreifende Verarbeitung geht also schief.

            Bevor wir auf Lösungsmöglichkeiten eingehen, würde ich gern wissen, ob wir nun inhaltlich beieinander sind.

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Hallo Rolf,

              dies ist Antwort 1 zu Deinem Post, weil ich sehe, dass es definitiv ein Verständnisproblem gibt:

              Es gibt 2 Tabelle:

              1: Grunddaten aller zu berechnenden Vorgänge
              2: Arbeitsdaten, die aus Tabelle 1 eingelesene Daten in Arbeitsvorgänge inkl. aller "Erledigt-Flags" beinhaltet.

              So, nun lese ich Deine Antwort weiter. Und danke für Deine Arbeit an meinem Problem. Bin da grad etwas peinlich berührt und daher besonderer Dank dafür.

              Jörg

            2. Hallo Rolf,

              Antwort 2:

              Wenn der Kunde 4711 neu ins System kommt und März '20 die erste Rechnung bekommt, müsste in deiner Table also ein Satz für ab_jahr 2020 und ab_monat 3 geschrieben werden. Wenn ich deine Idee richtig verstehe, ist das der Keim für dein Verfahren. Wenn Ende März 2020 der Cron-Job Läuft, findet er den Satz für März '20 und probiert nun Sätze für Jan, Feb und März 2020 zu schreiben (du beginnst ja bei 1 zu zählen). Jan und Feb werden durch deinen IF abgefangen. Im März geht es schief, weil der Satz schon da ist. Und für den April gelingt der Insert.

              Fast. Wenn der Kunde im März 20 neu kommt und der Cron Ende März zum erstem Mal läuft, liest das script aus Tabelle 1 den neuen Kunden ein, versucht für Jan und Feb Einträge vorzunehmen, was die If verhindert. März und April werden aber klappen. Anschließend greift die "for-Schleife" und verhindert im "Ende März", dass für Mai und folgende ein Eintrag in T2 versucht wird. Also wird über die "while-Schleife" nun der nächste Eintrag aus T1 verarbeitet.

              Soweit, so gut. Betrachten wir den nächsten Cron-Job, Ende April. Der findet den März 20. Die Schleife läuft los, Jan - nö. Feb - nö. März - dup. April - dup. Mai - Erfolg.

              Ja, genau.

              Als nächstes findet er den April 20. Und es geht von vorn los. Jan - nö. Feb - nö. März - dup. April - dup. Mai - dup. Das war vergebliche Liebesmühe.

              Nein, als nächstes findet er den nächsten eintrag aus T1 und beginnt die Monatsschleife wieder von vorne.

              Deswegen meinte Mudgard, du müsstest den jeweils jüngsten schon vorhandenen Satz finden, und ab diesem loslaufen.

              Und ich meine, ich fange einfach im Januar des aktuellen jahres an. Praxisrelevant ist ja, dass ohnehin die RGs monatlich raus ,müssen und wenn das script aus welchen Gründen auch immer mal in einem Monat versagt, muss ich ran. Ich lasse mich aber gerne besserer Löungen belehren.

              Betrachten wir einen anderen Fall. Die Tabelle enthält für Kunde 4711 genau einen Satz mit ab_jahr=2020 und ab_monat=12. Der nächste Cron-Job Ende Dezember startet. $Jahr ist bei Dir nun 2021 und $Monat ist 1.

              So konstruiert hättets Du recht, aber der Fall kann in der Praxis so nicht auftreten. Wenn ein Kunde neu eintritt, kann ich nicht erst ein Jahr später rückwirkend die erste RG schreiben. Soll heißen, wenn der Kunde ab 12/20 berechnet wird, findet der Cron (das Script) den Kunden im Dez. 20.

              Aber Du hast dennoch recht, Du hast eine Fehlerquelle aufgetan: Nämlich, wenn das Script im Dez. nen Bock schießt, werde ich im Januar so oder so ein Problem bekommen.

              Jahresübergreifende Verarbeitung geht also schief.

              Ja, genau hier sprichst Du es nochmal an... 👍

              Bevor wir auf Lösungsmöglichkeiten eingehen, würde ich gern wissen, ob wir nun inhaltlich beieinander sind.

              Ok, ich glaube, hier sind wir jetzt inhaltlich beieinander. Und gut, dass ich mal nachgefragt habe... ich hatte den Jahreswechsel übersehen. Hierzu denk ich mir jetzt mal was aus und melde mich. Es sei denn, Du oder Andreas hat hier eine Lösung quais auf den Lippen liegenb, dann nehm ich die auch gerne.

              Jörg

              1. Hallo Jörg,

                oookay. Du hast die Tabellen also normalisiert, das ist gut so.

                Dann ist das Ab-Datum in der Tabelle 1 aber ausschließlich relevant, wenn in der Tabelle 2 noch keine Sätze stehen. Oder wenn Rechnungen ausgesetzt werden sollen, der Kunde also bspw. für 2 Monate beitragsfrei gestellt werden soll.

                (Was meine ich: Der Kunde hat bis März 2021 Rechnungen bekommen, aus irgendeinem Grund soll er für April und Mai nicht bezahlen. Man könnte also das "ab" Datum in Tabelle 1 auf Juni 2021 stellen).

                Sobald für diesen Kunden in Tabelle 2 bereits Sätze vorhanden sind, solltest Du Dich nicht am Ab-Datum in Tabelle 1 orientieren, sondern an den vorhandenen Einträgen in Tabelle 2. Statt schön, stark und mutig[1] die Monate im laufenden Jahr durchzuprobieren, solltest Du alle relevanten Einträge in Tabelle 2 per SELECT holen, absteigend nach Jahr, Monat sortieren und mit LIMIT 1 dafür sorgen, dass Du nur den neuesten bekommst. Wenn Du keinen Eintrag findest, verwendest Du ab_jahr und ab_monat aus Tabelle 1.

                Damit hast Du deinen Startzeitpunkt T1. Speichere ihn in $LfdMonat und $LfdJahr.

                Deinen Endezeitpunkt T2 bestimmst Du wie geschrieben. Du speicherst ihn im Moment in $Monat und $Jahr; ich würde die Variablen $EndMonat und $EndJahr nennen, der Klarheit wegen. Du hast für Monat und Jahr jeweils zwei Variablen, dann sollte keine davon nur $Monat heißen.

                Und jetzt lässt Du für den aktuellen Tabelle-1 Satz eine Schleife laufen, solange T1 <= T2 ist. Wann ist ein Pärchen aus (monat,jahr) kleiner oder gleich einem anderen Pärchen?

                Fall 1: Jahr 1 ist kleiner als Jahr 2. Der Monat ist egal. Fall 2: Jahr 1 ist gleich Jahr 2 UND Monat 1 ist kleiner oder gleich Monat 2.

                Ich schlage damit diese PHP / Pseudocode Mischung als Lösung vor:

                $ende = ARRAY($EndMonat, $EndJahr);
                
                while($row_alle = mysqli_fetch_row($result_alle)) {
                   $letzteRechnung = ... // 
                   if (/* $letzteRechnung vorhanden */) {
                      $lfd = ARRAY($letzteRechnung[ /* monat-spalte */ ],
                                   $letzteRechnung[ /* jahr-spalte */ ]);
                      // Beginne mit dem Folgemonat
                      NächsterMonat($lfd);
                   } else {
                      // Erste Rechnung
                      $lfd = ARRAY($row_alle[2],  /* monat */
                                   $row_alle[3]);  /* jahr */
                   }
                   
                   while (VergleicheZeitpunkt($lfd, $ende <= 0) {
                      // Schreibe Satz für LfdMonat, LfdJahr in Tabelle 2
                
                      NächsterMonat($lfd);
                   }
                }
                
                function NächsterMonat(&$mj) {   // <-- Referenz!!!
                   $mj[0]++;
                   if ($m[0] > 12) {
                      $m[0] = 1;
                      $j[0]++;
                   }
                }
                
                // Liefert -1 wenn $mj1 < $mj2, 0 wenn gleich und 1 wenn >
                function VergleicheZeitpunkt($mj1, $mj2) {
                   // Spaceship Operator <=> ab PHP 7!
                   $ergebnis = $mj1[0] <=> $mj2[0];
                   return $ergebnis == 0 ? $ergebnis : $mj1[1] <=> $mj2[1];
                }
                

                Ich fasse Monat und Jahr in ein Array zusammen, weil man so einen m/j Zeitpunkt leicht als Parameter übergeben kann. Eine Klasse dafür zu machen wäre noch schicker, aber das ist nicht dein Ding, soweit ich weiß.

                Die beiden Helper-Funktionen machen den Code lesbarer und selbstdokumentierender (und wären gerne Methoden einer Klasse 😉). VergleicheZeitpunkt funktioniert so ab PHP 7, vor PHP 7 muss man es mühsamer mit 4 IF Abfragen machen.

                Den Zeitpunkt-Vergleich hattest Du bisher falsch. Das ist ein Tupelvergleich, die vergleicht man Wert für Wert.

                VergleicheZeitpunkt geht so vor, dass es zuerst die Jahreswerte vergleicht. Sind sie verschieden, ist der Monat nicht mehr relevant. Nur wenn sie gleich sind (<=> liefert für die Jahre 0), dürfen die Monate verglichen werden.

                Was hältst Du von diesem Plan?

                Rolf

                --
                sumpsi - posui - obstruxi

                1. schön gegen das Duplikat gerannt, stark zurückgeschleudert worden und mutig neu probiert ↩︎

                1. Antwort 1: Ich muss das mal heute abend oder morgen früh in die Praxis überführen, damit ich genau weiß, wo die Vorteile liegen. Ich melde mich dann wieder inhaltlich dazu, ok? Erstmal vielen Dank für die Hilfe!👍

                2. Hallo Rolf,

                  hm, zunächst ist da noch ein Syntaxfehler drin, den ich nicht finde. Expected } or ] .

                  Siehst du hier was?

                  Jörg

                  1. Hallo Jörg,

                    Siehst du hier was?

                    Ja. Sorry. Der Fehler ist bei mir auch drin. Das kommt davon, wenn man Code vom Hirn ins Forum rausrotzt und nicht vorher testet.

                    Der PHP Parser zeigt hier seine besondere „Stärke“ im Auffinden von Fehlern. Ich habe keine Ahnung, auf welchem Holzweg er da ist, wenn er an dieser Stelle an Stelle eines Semikolons ein } oder ] erwartet.

                    Vorgehensweise: Wenn eine Codezeile bemängelt wird, aber fehlerfrei aussieht, schau Dir die Code-Zeile davor an (also das while, nicht den Kommentar). Da hätte eine expected-Meldung hingehört. Allerdings nicht für } oder ], sondern für was anderes. Die weitere Fehlersuche überlasse ich dann als leichte Übung dem Leser.

                    Rolf

                    --
                    sumpsi - posui - obstruxi
                    1. Hallo Rolf,

                      Vorgehensweise: Wenn eine Codezeile bemängelt wird, aber fehlerfrei aussieht, schau Dir die Code-Zeile davor an (also das while, nicht den Kommentar). Da hätte eine expected-Meldung hingehört. Allerdings nicht für } oder ], sondern für was anderes. Die weitere Fehlersuche überlasse ich dann als leichte Übung dem Leser.

                      Arghs. Das hätte ich auch selber sehen können, hatte ein "Brett vorm Kopf". 😇

                      Weiter im Text... 😉

                3. Hallo Rolf,

                  function NächsterMonat(&$mj) { // <-- Referenz!!! $mj[0]++; if ($m[0] > 12) { $m[0] = 1; $j[0]++; } }

                  Ich versteh noch nicht, wie das mit Referenz als Parameter funktioniert und warum Du auf $m und $j zugreifen kannst. Wo kommen die denn her?🤔

                  Jörg

                  1. Hallo Jörg,

                    aus einem Tippfehler. Ich hab das ein paar mal geändert. Es muss natürlich immer $mj heißen.

                    Übernimm meinen Code nicht unverstanden. Wie Du siehst, ist das riskant 😂

                    Was heißt Referenz? Lektüre

                    Zusammengefasst:

                    PHP speichert jeden Wert, den es verarbeitet, in einer bestimmten Datenstruktur. Jeden. Zahlen, Bools, Strings, Arrays, Objekte. Deine Variablen enthalten nicht die Werte selbst, sondern einen Verweis auf eine solche Datenstruktur, die PHP irgendwo im Hintergrund verwaltet.

                    $a = 7;
                    

                    Dadurch werden 2 Dinge erzeugt: Die Datenstruktur, die Werte speichern kann (hier vom Typ INT und dem Wert 7), sowie die Variable $a, in der nur steht, wo sich diese Datenstruktur befindet.

                    Wenn Du $a an eine Funktion übergibst, wird nicht die Datenstruktur übergeben. Sondern die Info, wo sie sich befindet.

                    $a = 7;
                    foo($a);
                    echo $a;
                    
                    function foo($b) {
                       $b = 9;
                    }
                    

                    D.h. wenn foo aufgerufen wird, steht in $b erstmal (fast) die gleiche Info drin wie in $a, nämlich die Position dieses int-Wertes 7. Das wäre so nicht gut, denn dann würde die Zuweisung, die in foo geschieht, diesen Wert verändern und der echo den Wert 9 ausgeben.

                    Sowas kann Absicht sein. Man spricht dann von einer Datenübergabe "by reference". Aber in den gängigen Programmiersprachen ist diese Form der Datenübergabe unerwünscht. Man möchte vielmehr nur den Wert übergeben und der Funktion nicht erlauben, Daten des Aufrufers zu verändern. Dann nennt man Übergabe "per value".

                    PHP übergibt aber trotzdem nur die Info, wo die 7 steht. Nun kommt das "fast" zum Tragen, was ich eben in Klammern schrieb. In $b steht nämlich zum einen die Info, wo der Wert steht, und zum anderen die Info: "NICHT ÄNDERN". Und das führt dazu, dass beim Versuch, $b zu verändern, die Wertestruktur mit der 7 darin kopiert wird und die 9 in die Kopie geschrieben wird. PHP nennt das "copy on write".

                    Das ist effizient. Solange nämlich die Parameterwerte nicht verändert werden, braucht PHP keine neue Wertestruktur anzulegen. Denn das macht Arbeit, das belegt Heap-Speicher, der nur für einen Moment gebraucht wird. Und wenn ich ein Array mit drölftausend ELementen übergebe, dann kostet das richtig viel Speicher. Für nix, wenn es nur gelesen wird.

                    Wenn ich also NächsterMonat "einfach so" programmiere und aufrufe, kann ich $mj ändern, wie ich will, die Änderung kommt beim Aufrufer nicht an. Weil der copy-on-write dafür sorgt, dass meine Änderungen am Array nur auf einer Kopie stattfinden.

                    Lösung 1: Ich gebe das veränderte $mj mit return zurück. Ich müsste NächsterMonat dann so aufrufen:

                    $lfd = NächsterMonat($lfd);
                    

                    Das würde gehen. Eine Inkarnation des Arrays geht hinein, würde kopiert, geändert und eine andere Inkarnation kommt heraus. Aber es ist nicht effizient, weil für jede Änderung ein neues Array angelegt werden muss.

                    Auftritt der Referenz. Hier wird diese Info "NICHT ÄNDERN" nicht gesetzt. Gesteuert wird das durch das & in der Definition von NächsterMonat. Wenn ich dann NächsterMonat($lfd) aufrufe, enthält $mj exakt das Gleiche wie $lfd, und es gibt keinen copy-on-write. Es passiert aber was anderes. Die Wertestruktur, die das Array speichert, enthält nämlich nicht nur den Wert, sondern auch eine Info, wieviele Leute diesen Wert ändern dürfen. Dort steht normalerweise 1, aber wenn eine Referenz erzeugt wird, erhöht sich das. PHP braucht diese Info, um zu wissen, wann der Speicherplatz für's Array freigegeben werden kann.

                    Und jetzt kann NächsterMonat das übergebene Array verändern, und die Änderung ist ohne eine Kopierflut der ZEND Engine beim Aufrufer verfügbar.

                    Alles weiter in der verlinkten Lektüre 😉

                    Rolf

                    --
                    sumpsi - posui - obstruxi
                    1. Nachtrag: Das sind Innereien der ZEND-Engine und das war alles mal auf PHP.NET dokumentiert. Zumindest war die Doku verlinkt. Irgendwie scheint das verschwunden zu sein.

                      Verfaulende Dokumentation auch bei PHP, nicht nur bei Microsoft?!

                      Rolf

                      --
                      sumpsi - posui - obstruxi
                    2. Nur mal kurz an dieser Stelle: Danke. 👍😀 Ich studiere noch Deinen Eintrag und entsprechende weiterführende Literatur, ich wollte mich lediglich mal rückmelden, bevor der Thread nicht mehr aktuell ist.

                      Jörg

            3. Hallo Rolf,

              Das ist die Zusatztabelle, von der ich weiter oben sprach.

              Naja, das Forum zeigt es "weiter unten" 😉

              es gibt die Möglichkeit, Beiträge auch chronologisch rückwärts, also von unten nach oben sortiert anzuzeigen. Dämlicherweise ist das sogar die Defaulteinstellung. Eventuell verwendest du auch diese Rückwärts-Sortierung.

              Jörgs zwei Antworten auf deinen Beitrag erscheinen bei mir jedenfalls in ihrer natürlichen Reihenfolge, also erst Antwort 1, dann darunter Antwort 2.

              Live long and pros healthy,
               Martin

              --
              Fische, die bellen, beißen nicht.
              1. Hallo Martin,

                "sortiere Beiträge" steht bei mir auf "aufsteigend" und ich verwende die Nested-Ansicht. Neue Beiträge können bei mir also im ganzen Threadbaum irgendwo verteilt sein.

                Aber ja, ich habe vergessen, dass CForum stark personalisierbar ist und das anderswo anders sein kann.

                Rolf

                --
                sumpsi - posui - obstruxi
                1. Hallo,

                  "sortiere Beiträge" steht bei mir auf "aufsteigend"

                  bei mir auch, und dabei ist das genau verkehrt bezeichnet. In Wirklichkeit sind sie dann von oben nach unten, also absteigend sortiert.

                  und ich verwende die Nested-Ansicht.

                  Uah, mit der kann ich mich gar nicht anfreunden.

                  Aber ja, ich habe vergessen, dass CForum stark personalisierbar ist und das anderswo anders sein kann.

                  Ja, das ist wohl so.

                  Live long and pros healthy,
                   Martin

                  --
                  Fische, die bellen, beißen nicht.
                  1. Hallo Martin,

                    das ist mein Threadbaum in der Einzel-Ansicht

                    "Aufsteigend sortiert" bedeutet für mich: In einer Liste steht der größere Wert weiter unten. Diese Bedingung ist aus meiner Sicht jeweils für die direkten Antworten auf ein Posting (also für alle Childnodes eines Posting-Nodes) gegeben.

                    Meine Settings:

                    Rolf

                    --
                    sumpsi - posui - obstruxi
                    1. Hallo Rolf,

                      das ist mein Threadbaum in der Einzel-Ansicht

                      sieht bei mir entsprechend aus.

                      "Aufsteigend sortiert" bedeutet für mich: In einer Liste steht der größere Wert weiter unten.

                      "Aufsteigend sortiert" bedeutet für mich: Wenn ich den Werten in ihrer natürlichen Reihefolge nachgehe, bewege ich mich von unten nach oben.

                      Live long and pros healthy,
                       Martin

                      --
                      Fische, die bellen, beißen nicht.
                      1. Hallo Martin,

                        sorry, aber ich glaube, damit bist Du ziemlich allein. Zumindest ist jedes IT Tool auf dieser Welt anderer Meinung.

                        Wenn ein Computerprogramm eine Liste von Objekten konsumiert (Sätze einer Datei, Buchstaben eines Wortes, Rows einer Query), dann sind die Werte aufsteigend, wenn für je 2 Werte (a,b) gilt: Wenn a früher als b konsumiert wird, gilt für die Werte die Relation a <= b.

                        Ersetze Computerprogramm durch Mensch.

                        Definiere früher: a wird früher als b konsumiert, wenn ich beim Lesen der Liste zuerst a antreffe und dann erst b.

                        Wie liest ein Mensch im westlichen Kulturkreis:

                        • Von links nach rechts
                        • Von oben nach unten

                        Du nicht?

                        Rolf

                        --
                        sumpsi - posui - obstruxi
                        1. Dieser Beitrag wurde gelöscht: Der Beitrag ist unkonstruktiv oder provokativ und trägt zu einer Verschlechterung der Stimmung bei.
                        2. Hi,

                          Wenn ein Computerprogramm eine Liste von Objekten konsumiert (Sätze einer Datei, Buchstaben eines Wortes, Rows einer Query), dann sind die Werte aufsteigend, wenn für je 2 Werte (a,b) gilt: Wenn a früher als b konsumiert wird, gilt für die Werte die Relation a <= b.

                          ja, einverstanden.

                          Ersetze Computerprogramm durch Mensch.

                          Definiere früher: a wird früher als b konsumiert, wenn ich es beim Lesen der Liste a vor b antreffe.

                          Korrekt.

                          Wie liest ein Mensch im westlichen Kulturkreis:

                          • Von links nach rechts
                          • Von oben nach unten

                          Auch da sind wir d'accord.

                          Aber "aufsteigend" oder "absteigend" bezeichnet doch eine geometrische oder räumliche Anordnung. Ist also b>a, dann bedeutet "aufsteigend", dass ich mich nach oben bewege, wenn ich von a zu b gehe.

                          Live long and pros healthy,
                           Martin

                          --
                          Fische, die bellen, beißen nicht.
                          1. Hallo Martin,

                            Aber "aufsteigend" oder "absteigend" bezeichnet doch eine geometrische oder räumliche Anordnung.

                            Sicher.

                            Aber die Geometrie einer Liste ist von der Geometrie eines Stapels verschieden. Wenn Du Scheiben mit a,b,c,d beschriftest und in Buchstabenfolge aufeinanderstapelst, dass a unten liegt und d oben, wäre das auch aufsteigend.

                            Denke statt dessen an Mathematik.

                            Eine Funktion ist monoton steigend (oder: hat aufsteigende Werte), wenn $$x_1 < x_2 \Rightarrow f(x_1) \le f(x_2)$$ gilt.

                            Bei einem Stapel bedeutet $$x_1<x_2$$, dass $$x_1$$ weiter unten ist. In einer Liste bedeutet es, dass Du $$x_1$$ beim Lesen vorher antriffst.

                            Rolf

                            --
                            sumpsi - posui - obstruxi
                      2. Hi Martin,

                        "Aufsteigend sortiert" bedeutet für mich: Wenn ich den Werten in ihrer natürlichen Reihefolge nachgehe, bewege ich mich von unten nach oben.

                        Heißt, wenn Du bei Amazon o.ä. die Artikel nach Preisen aufsteigend sortierst, erwartest Du den teuersten Artikel ganz oben?

                        Aber "aufsteigend" oder "absteigend" bezeichnet doch eine geometrische oder räumliche Anordnung.

                        Wenn Du eine Ziffernreihe kommabasiert nach Wert aufsteigend sortierst und sie stehen dann nebeneinander, was dann? Würdest Du sie dann als unsortiert bezeichnen? 😉

                        Jörg

                        1. Hallo Jörg,

                          "Aufsteigend sortiert" bedeutet für mich: Wenn ich den Werten in ihrer natürlichen Reihefolge nachgehe, bewege ich mich von unten nach oben.

                          Heißt, wenn Du bei Amazon o.ä. die Artikel nach Preisen aufsteigend sortierst, erwartest Du den teuersten Artikel ganz oben?

                          richtig, genau das.
                          Und wenn ich in Thunderbird die Mails im Posteingang aufsteigend nach Eingangsdatum sortiere, erwarte ich auch die neueste Nachricht oben (für mich eine widernatürliche Sortierung, ich erwarte eine chronologische Abfolge immer von oben nach unten, also absteigend).

                          Aber "aufsteigend" oder "absteigend" bezeichnet doch eine geometrische oder räumliche Anordnung.

                          Wenn Du eine Ziffernreihe kommabasiert nach Wert aufsteigend sortierst und sie stehen dann nebeneinander, was dann? Würdest Du sie dann als unsortiert bezeichnen? 😉

                          Betrachte in dem Fall "weiter rechts" als gleichrangig zu "weiter oben".
                          Wie im Koordinatensystem.

                          Live long and pros healthy,
                           Martin

                          --
                          Fische, die bellen, beißen nicht.
                          1. Hi Martin,

                            lustig. Bist du damit im beruflichen Umfeld schonmal in blöde Situationen mit Kollegen, Geschäftspartnern oder Vorgesetzten gekommen oder preist Du das dann schon ein, dass es "da nicht einen, sondern Millionen Geisterfahrer gibt" und siehst milde über all die "schräg denkenden Mitmenschen" hinweg? 😊😉

                            Jörg

                            1. Hallo,

                              lustig. Bist du damit im beruflichen Umfeld schonmal in blöde Situationen mit Kollegen, Geschäftspartnern oder Vorgesetzten gekommen oder preist Du das dann schon ein, dass es "da nicht einen, sondern Millionen Geisterfahrer gibt" und siehst milde über all die "schräg denkenden Mitmenschen" hinweg? 😊😉

                              nichts von alledem. Ich weiß, dass die Formulierungen "aufsteigend" oder "absteigend" sortieren nicht von jedem gleich verstanden werden (ich bin mit meiner Ansicht ja kein Einzelfall), und frage bei Zweifeln lieber noch einmal nach.

                              Und freue mich, wenn manche Websites diese Begriffe vermeiden und stattdessen lieber Symbole mit Aufschriften wie etwa A→Z oder Z→A verwenden (oder die vertikale Variante). Das ist eindeutig und unmissverständlich.

                              Live long and pros healthy,
                               Martin

                              --
                              Fische, die bellen, beißen nicht.
                              1. Hi Martin,

                                nichts von alledem. Ich weiß, dass die Formulierungen "aufsteigend" oder "absteigend" sortieren nicht von jedem gleich verstanden werden (ich bin mit meiner Ansicht ja kein Einzelfall), und frage bei Zweifeln lieber noch einmal nach.

                                Hätte nie gedacht, dass es hier Diskussionspotential gibt. Ach, gibts für mich auch eigentlich nicht, aber zumindest kann ich Deine Argumentation ein Stückweit nachvollziehen.😊

                                Jörg

  2. Hi,

    ich möchte in eine Tabelle Monate ab einem bestimmten Zeitpunkt eintragen.

    ist das eine persistente Tabelle?

    Dann müßtest Du, bevor Du einzutragen versuchst, erst mal den neuesten Eintrag ermitteln.

    Ich möchte aber, dass ich niemals den nächsten Monat vor dem 20. des laufenden Monats eintrage.

    Ok, der Endzeitpunkt ist also entweder der vorige oder der jetzige Monat.

    for($i = 1;$i <= $Monat;$i++) {
    

    Damit bekommst Du maximal 12 Monate in Deine Tabelle, angefangen mit Januar.

    Was, wenn der letzte Lauf im Juli des vergangenen Jahres stattgefunden hat?

    Mein Ansatz wäre:

    • Ermittle das Start-Datum (fix oder aus der bestehenden Tabelle)
    • Ermittle das Ende-Datum (1. Tag von diesem/vorigem Monat abhängig vom Tag im Monat)
    • aus dem Start-Datum erzeuge das Datum für den 1. des Start-Datum-Monats (sei Start-Datum der 17.06.2019, dann nimm den 01.06.2019)
    • aktuellesDatum = startDatum (mit Tag = 1)
    • solange aktuellesDatum <= endeDatum, erzeuge den Eintrag, und addiere 1 Monat auf das aktuelle Datum

    Den 1. des Monats würde ich für das Datum verwenden anstelle des aktuellen Tags, da das sonst ggf. Probleme gibt: was ist der 31. Januar + 1 Monat? Der 31. Februar? Der 3. März? Der 28. Februar (bzw. im Schaltjahr der 29. Februar?)

    cu,
    Andreas a/k/a MudGuard

    1. Hallo MudGuard,

      ist das eine persistente Tabelle?

      Nein! Doch! Oh!

      Du denkst an eine SQL Tabelle? Ich dachte an die Ausgabe einer Tabelle im HTML. Aber es könnte auch ein Array in PHP sein...

      Jörg? Wo genau willst Du was eintragen?

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Hi,

        ist das eine persistente Tabelle?

        Nein! Doch! Oh!

        Du denkst an eine SQL Tabelle?

        Naja, das "zudem soll der Prozess jederzeit gestartet, pausiert und ggf. nach Monaten fortgesetzt werden können. " ließ mich an eine persistente Tabelle denken.

        cu,
        Andreas a/k/a MudGuard

      2. Nein! Doch! Oh!

        Du denkst an eine SQL Tabelle?

        Ja. Eine SQL Tabelle.

        Jörg

    2. Hallo Andreas,

      ist das eine persistente Tabelle?

      Ja, SQL.

      Dann müßtest Du, bevor Du einzutragen versuchst, erst mal den neuesten Eintrag ermitteln.

      Nicht zwingend. Ich trage einfach ein und habe zudem einen zusammengesetzten Schlüssel, der Doppeleinträge verhindert und stattdessen einen Error 1062 ausspuckt. Sorry, hatte ich vergessen, einfgangs zu erwähnen.

      Ok, der Endzeitpunkt ist also entweder der vorige oder der jetzige Monat.

      Nein, entweder der aktuelle oder der nächste Monat. Hintergrund ist ein Script, das automatisierte Rechnungen erstellt. Die sollen aber nie vor dem 20. des aktuellen monats für den nächsten Monat raus gehen.

      Damit bekommst Du maximal 12 Monate in Deine Tabelle, angefangen mit Januar.

      Ich habe mal Code nachgeliefert, in den auch das Jahr implementiert ist.,

      Mein Ansatz wäre:

      • Ermittle das Start-Datum (fix oder aus der bestehenden Tabelle)
      • Ermittle das Ende-Datum (1. Tag von diesem/vorigem Monat abhängig vom Tag im Monat)
      • aus dem Start-Datum erzeuge das Datum für den 1. des Start-Datum-Monats (sei Start-Datum der 17.06.2019, dann nimm den 01.06.2019)
      • aktuellesDatum = startDatum (mit Tag = 1)
      • solange aktuellesDatum <= endeDatum, erzeuge den Eintrag, und addiere 1 Monat auf das aktuelle Datum

      Den 1. des Monats würde ich für das Datum verwenden anstelle des aktuellen Tags, da das sonst ggf. Probleme gibt: was ist der 31. Januar + 1 Monat? Der 31. Februar? Der 3. März? Der 28. Februar (bzw. im Schaltjahr der 29. Februar?)

      Mein Ansatz ist:

      Ich erzeuge eine Tabelle, die alle zu berechnenden Einträge beinhaltet. Diese Tabelle enthält Flags für "erstellten Vorgeng", "erstellte Rechnung", "versandte Email" und hat einen Schlüssel, der Doppler vermeidet.

      Anschließend arbeite ich die Tabelle ab und upodate entsprechend die Flags. Das Script kann über Cronjob am Monatsende gestartet werden.

      Das nur Einträge für den Folgemonat in die Tabelle aufgenommen werden dürfen, ist hauptsächlich für Testzwecke wichtig.

      Jörg