Pit: csv Datei von Duplikaten befreien

0 100

csv Datei von Duplikaten befreien

Pit
  • php
  1. 0
    Auge
    1. 0
      Pit
      1. 0
        Der Martin
  2. 0
    MudGuard
    1. 0
      Pit
  3. 0

    Kommt drauf an...

    Raktenwissenschaftler
    1. 0
      Raketenwissenschaftler
  4. 0

    csv Datei von Duplikaten befreien / Neue Frage

    Pit
    1. 0
      Pit
      1. 0
        Raketenwissenschaftler
        1. 0
          Raketenwissenschaftler
          • sql
        2. 0
          Tabellenkalk
        3. 0
          Rolf B
          1. 0
            Raketenwissenschaftler
          2. 0
            Pit
            • danke
            • php
            1. 0
              Rolf B
              1. 0
                Pit
                1. 0
                  Rolf B
                  1. 0
                    Pit
                    1. 0
                      Rolf B
                      1. 0
                        Pit
                        1. 0
                          Rolf B
                          1. 0
                            Pit
                            1. 0

                              csv Datei von Duplikaten befreien / Neue Frage und Antwort

                              Pit
                              1. 0
                                Rolf B
                                1. 0
                                  Pit
                                  1. 0
                                    Pit
                                    1. 1
                                      Rolf B
                                      1. 0
                                        Pit
                                        1. 0
                                          Rolf B
                                          1. 0
                                            Pit
                                            1. 0
                                              Rolf B
                                              1. 0
                                                Pit
                                            2. 0
                                              Rolf B
                                              1. 0
                                                Pit
                                                1. 0
                                                  Rolf B
                                                  1. 0
                                                    Pit
                                                    1. 0
                                                      Pit
                                                      1. 0
                                                        Raketenwissenschaftler
                                                        1. 0
                                                          Pit
                                                          1. 0
                                                            Raketenwissenschaftler
                                                            1. 0
                                                              Pit
                                                            2. 0
                                                              Rolf B
                                                              1. 0
                                                                Pit
                                                                1. 0
                                                                  Rolf B
                                                                  1. 0
                                                                    Matthias Apsel
                                                                    1. 0
                                                                      Tabellenkalk
                                                                      1. 0
                                                                        MudGuard
                                                                    2. 0
                                                                      Rolf B
                                                                  2. 0
                                                                    Pit
                                                          2. 1
                                                            Felix Riesterer
                                                            • php
                                                            1. 0
                                                              Pit
                                                              1. 0
                                                                Felix Riesterer
                                                                1. 0
                                                                  Pit
                                                                  1. 1

                                                                    NoSQL?

                                                                    Raketenwissenschaftler
                                                                    1. 0

                                                                      NoSQL? (Korrektur)

                                                                      Raketenwissenschaftler
    2. 0
      Raketenwissenschaftler
      1. 0
        Pit
        1. 0
          Raketenwissenschaftler
      2. 0
        Raketenwissenschaftler
    3. 0
      Felix Riesterer
      1. 0
        Pit
        1. 0
          Tabellenkalk
          1. 3
            Raketenwissenschaftler
            1. 2
              Tabellenkalk
        2. 0
          Felix Riesterer
          1. 0
            Pit
    4. 0
      pl
  5. 1

    Kritik an Deinem Umgang mit Hilfe

    Felix Riesterer
    1. 3
      Pit
      1. 1
        Der Martin
        1. 0
          pl
          1. 0
            Tabellenkalk
            1. 0
              Der Martin
            2. 0
              pl
        2. 0

          Zeilenumbruch in einem CSV-Feld

          Linuchs
          • programmiertechnik
          1. 0
            MudGuard
          2. 0
            Der Martin
            1. 0
              Rolf B
              1. 0
                Der Martin
          3. 0
            pl
            1. 0
              Felix Riesterer
              1. 0
                pl
              2. 0
                pl
              3. 1
                Christian Kruse
                1. 0
                  Rolf B
                  1. 0
                    Christian Kruse
                    1. 0
                      Der Martin
                      1. 0
                        Christian Kruse
                        1. 0
                          Der Martin
                    2. 0
                      Rolf B
                      1. 0
                        Christian Kruse
            2. 0
              Rolf B
              1. 0

                Zeilenumbruch in einer textarea

                pl
      2. 0
        Felix Riesterer
      3. 0
        Rolf B
      4. 0
        pl
        1. 0
          pl
      5. 0
        Raketenwissenschaftler

Hallo,

ich will mir gerade eine Funktion schreiben, die eine CSV-Datei von Duplikaten befreit.

Die Datei ist wie folgt aufgebaut:

Feld1|Feld2|Feld3|Feld4|Feld5
Feld1|Feld2|Feld3|Feld4|Feld5
Feld1|Feld2|Feld3|Feld4|Feld5
Feld1|Feld2|Feld3|Feld4|Feld5
Feld1|Feld2|Feld3|Feld4|Feld5
...

und kann durchaus sehr groß sein.

Deshalb komme ich ein bischen ins Schwimmen beim Bauen der Funktion. Kann man das so anfangen?

// CSV-Datei auf Unikate bereinigen
$myCsvArray = explode("\n",file_get_contents($csv_datei));
$myCsvArray = array_unique($myCsvArray);
// Original löschen
if(file_exists($csv_datei)) {
    unlink($csv_datei);
}
    // Neue Datei schreiben
...

Und wie schreibe ich die Daten dann am besten wieder in die Datei?

$datei = fopen("csv_datei.csv","a");
fwrite($datei, ??? );
fclose($datei);

Wer kann mir mal unter die Arme greifen?

Pit

  1. Hallo

    ich will mir gerade eine Funktion schreiben, die eine CSV-Datei von Duplikaten befreit.

    Kann man das so anfangen?

    // CSV-Datei auf Unikate bereinigen
    $myCsvArray = explode("\n",file_get_contents($csv_datei));
    

    geht auch in einem Rutsch mit file. Die Funktion liest jede Zeile in ein Array-Element ein.

    // CSV-Datei auf Unikate bereinigen
    $myCsvArray = file($csv_datei);
    
    $myCsvArray = array_unique($myCsvArray);
    

    Das sieht soweit grundsätzlich richtig aus.

    // Original löschen
    if(file_exists($csv_datei)) {
        unlink($csv_datei);
    }
        // Neue Datei schreiben
    ...
    

    Du kannst die Datei auch überschreiben. Damit brauchst du diesen Block nimmer mehr.

    Und wie schreibe ich die Daten dann am besten wieder in die Datei?

    $datei = fopen("csv_datei.csv","a");
    fwrite($datei, ??? );
    fclose($datei);
    

    Wie oben gesagt, du kannst die Datei auch überschreiben, ohne sie vorher zu löschen. Dazu brauchst du einen anderen Modus zum öffnen der Datei als "a". Der Modus "w" bietet sich an, da die Datei auf die Länge 0 gebracht wird und der Zeiger/Cursor damit am Anfang der nun leeren Datei gesetzt wird.

    Aus der Doku zu fopen (Abschnitt „mode“ [1], Modus "w"): „Nur zum Schreiben geöffnet; platziere Dateizeiger auf Dateianfang und kürze die Datei auf eine Länge von 0. Existiert die Datei nicht, versuche, diese zu erzeugen.“

    Weiterhin musst du dafür sorgen, dass in der Zeit der Schreiboperation niemand anderes auf die Datei zugreifen kann. Das kannst du mit der Funktion flock erreichen.

    $datei = fopen("csv_datei.csv", "w");
    if (flock($datei, LOCK_EX)) {
        fwrite($datei, implode("\n", $myCsvArray));
        flock($datei, LOCK_UN);
    }
    fclose($datei);
    

    [edit]: Der Volsltändigkeit halber sei hinzugefügt, dass die Alternative zu fopen, flock, fwrite und fclose die Verwendung von file_put_contents ist, die diesen Block in einer Funktion vereinigt.[/edit]

    Also den Inhalt mit file einlesen, ihn mit array_unique behandeln und ihn zu guter Letzt mit dem Block direkt hier drüber [edit](oder mit file_put_contents)[/edit] wieder in die Datei schreiben.

    Tschö, Auge

    --
    Ein echtes Alchimistenlabor musste voll mit Glasgefäßen sein, die so aussahen, als wären sie beim öffentlichen Schluckaufwettbewerb der Glasbläsergilde entstanden.
    Hohle Köpfe von Terry Pratchett

    1. Früher™️ gab es für Abschnitte einer PHP-Doku-Seite IDs, auf die man verlinken konnte. Nun nicht mehr. 👎 😟 ↩︎

    1. Hallo Auge,

      vielen Dank für Deine Unterstützung!

      Ich kenne file. Ich nutze es nicht, weil mir mal irgendwer (ich glaube sogar, es war hier) gesagt hat, dass file für große Dateien nicht so gut wäre. Ist das so?

      Perfekt, den w-Modus zu nutzen, das spart den unlink-Schritt tatsächlich.

      flock ist (in diesem Fall) unnötig, weil nur ich das Script nutze, aber grundsätzlich danke für den Hinweis!

      Und das zurückschreiben in die Datei über implode ist natürlich genial und ich ärgere mich ein bischen, dass ich da nicht selber drauf gekommen bin.

      Alles in allem: Vielen Dank für Deine Hilfe!

      Pit

      1. Hallo,

        Ich kenne file. Ich nutze es nicht, weil mir mal irgendwer (ich glaube sogar, es war hier) gesagt hat, dass file für große Dateien nicht so gut wäre. Ist das so?

        ich weiß nicht, was der Hintergedanke zu dieser Empfehlung war. Vermutlich der hohe Arbeitsspeicher-Bedarf. Wer immer dir das geraten hat, meinte das wahrscheinlich im Vergleich zum Zeile-für-Zeile-Verarbeiten. Denn sowohl file() als auch file_get_contents() lesen den Dateiinhalt komplett in den Arbeitsspeicher.

        Und das zurückschreiben in die Datei über implode ist natürlich genial und ich ärgere mich ein bischen, dass ich da nicht selber drauf gekommen bin.

        Wenn du von der Fraktion "Warum einfach, wenn's auch umständlich geht" bist, kannst du natürlich auch mit einer for- oder foreach-Schleife über dein Array iterieren, jede Zeile einzeln mit fwrite() schreiben und ein "\n" anhängen.
        (Ironie-Tags bei Bedarf selbst setzen.)

        Ciao,
         Martin

        --
        Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
  2. Hi,

    if(file_exists($csv_datei)) {
        unlink($csv_datei);
    }
        // Neue Datei schreiben
    

    Gefährlich. Laß irgendwas genau jetzt nach dem Löschen der alten Datei und vor dem (vollständigen) Schreiben der neuen Datei schiefgehen, dann sind die Daten weg.

    Besser: neue Datei unter anderem Namen schreiben, wenn fertig, alte Datei löschen, neue Datei umbenennen.

    cu,
    Andreas a/k/a MudGuard

    1. Danke für den Hinweis! Pit

  3. und kann durchaus sehr groß sein.

    Hm. Wenn ess sich um eine private Anwendung (keine zu veröffentlichende Software) handelt, die Datei durch einen technischen Prozess erzeugt wird und immer einen exakt gleichen Aufbau hat

    Feld1|Feld2|Feld3|Feld4|Feld5
    Feld1|Feld2|Feld3|Feld4|Feld5
    

    also nicht etwa etwas wie

    Feld1|Feld2|Feld3|Feld4|Feld5
    Feld1|Feld2|"Feld3"|Feld4|Feld5
    Feld1|Feld2 |Feld3|Feld4|Feld5
     Feld1|Feld2|Feld3|Feld4|Feld5
    

    vorkommen kann, und Linux/BSD als OS fest steht, dann (und genau dann) würde ich die Performance messen, mit der PHP oder ein vorheriges sort -u datei (Linux) die doppelten Datensätze ausfiltert.

    $sys = "sort -u '$csv_datei'";
    $myCsvArray = explode("\n", `$sys` );
    

    Ansonsten: das von Dir gezeigte array_unique() scheitert ebenso am roten Beispiel wie der Linux/Unix-Befehl sort...

    1. Vermutlich würde ich sogar zu:

      $sys = "sort -u 'original.csv' 1>'unique.csv' 2>'logfile.txt'; echo $?";
      

      greifen. Ist nach einem

      $err = intval(`$sys`);
      

      in $err etwas anderes als 0, dann ging was schief und Du kannst in logfile.txt nachlesen was oder sogar einen Error triggern:

      trigger_error( file_get_contents( 'logfile.txt' ), E_USER_ERROR );
      
  4. Hallo nochmal,

    nachdem das Entfernen von Duplikaten, Triplikaten, Quadrokaten und dergleichen mehr nun wunderbar funktioniert, habe ich festgestellt, dass mein Problem doch etwas tiefgreifender ist:

    Oft sind es gar keine echten Duplikate, die mich in den csv-Dateien stören. Denn für meine Nöte sind eigendlich nur 2 Felder der csv-Datei zuständig, also hier zum Beispiel Feld2 und Feld3. D.h., wenn Feld2 und Feld3 einer Zeile mit Feld2 und Feld3 einer anderen zeile identisch sind, würde ich gerne beide Datensätze aus der Datei entfernen.

    Feld1|Feld2|Feld3|Feld4|Feld5
    Feld1|Feld2|Feld3|Feld4|Feld5
    Feld1|Feld2|Feld3|Feld4|Feld5
    Feld1|Feld2|Feld3|Feld4|Feld5
    Feld1|Feld2|Feld3|Feld4|Feld5
    ...
    

    Gibt es da eine intelligente und performamnte Lösung, ohne alle Datensätze iterieren zu müssen oder falls nicht, wie stellt man sowas dann trotzdem möglichst effizient an?

    Pit

    1. Gibt es da eine intelligente und performamnte Lösung, ohne alle Datensätze iterieren zu müssen oder falls nicht, wie stellt man sowas dann trotzdem möglichst effizient an?

      Vor allem, es würde auch ausreichen, das Ganze per mysql zu machen, da ich die erzeugte Datei in mysql importiere.

      Das heißt also, wenn ich eine Query hätte, die anhand zweier Spalten feststellt, dass der Eintrag (in für mich relevanten Spalten) gleich ist und beide entfernt, wäre mir auch gedient.

      Pit

      1. Das heißt also, wenn ich eine Query hätte

        Du kannst in der Tabellendefinition einen Unique-Index über die beiden Spalten setzen. Falls diese beiden Spalten nur pro Import "unique" sein müssen, dann nimm eine temporäre Tabelle und schreibe deren Inhalt in einem zweiten Schritt in die eigentliche Tabelle.

        Aber eigentlich würde ich wohl zu sort greifen. Frage: Was meinst Du mit "große Datei"?

        1. Und wenn wir schon dabei sind:

          Statt die Datei via PHP Zeile für Zeile und Wert für Wert in die Datenbank-Tabelle mit dem Unique-Index zu "schubsen" kannst Du auch innerhalb von PHP und einer bestehenden MYSQL-Verbindung zum

          LOAD DATA INFILE

          Statement greifen. Allerdings solltest Du dann auch hier lesen.

        2. Hallo,

          Unique-Index

          Das Löschen beider Datensätze, wenn 2 Felder identisch sind, ist eher ungewöhnlich. Es würde mich überraschen, wenn Datenbanken eine out-of-the-box-Lösung hätten.

          Möglicherweise gehts mit einem Trigger, der im Fall, dass der Unique-Index aktiv werden muss, einen Marker setzt. Anschließend dann markierte Datensätze löschen. Aber wie so eine Trigger aussehen muss, weiß ich auch nicht…

          Gruß
          Kalk

        3. Hallo Raketenwissenschaftler,

          Du kannst in der Tabellendefinition einen Unique-Index über die beiden Spalten setzen.

          Das hilft nicht, wenn die Daten Duplicates enthalten. Dann bricht der Import ab. Glaube ich...

          TIL[1] : row-value-expressions.

          Und damit wäre mein Ansatz dieser: eine Work-Table mit einen NON-UNIQUE Index, so dass Duplicates erstmal kein Problem sind, aber ein GROUP BY effizient via Index möglich ist. Und dann die Duplikate ausblenden. Das geht mit row-value-expressions und NOT IN.

          Angenommen, die Tabelle, in die der wirkliche Import stattfinden soll, hieße real_table. Dann gelingt es mit folgender Magie:

          INSERT INTO real_table (feld1, feld2, feld3, feld4, feld5, feld6)
          SELECT feld1, feld2, feld3, feld4, feld5, feld6
          FROM temp_table
          WHERE (feld2, feld3) NOT IN (SELECT feld2, feld3
                                       FROM temp_table
                                       GROUP BY feld2, feld3
                                       HAVING COUNT(*) > 1)
          

          (feld2, feld3) ist eine row-value-expression. Sowas kannte ich bisher nur vom DB2 unseres Großrechners, und hielt es für eine nützliche, aber ansonsten unbekannte IBM Erweiterung. Weit gefehlt, das ist SQL-92 und es funktioniert z.B. auch in MYSQL 5.6.

          Soviel zu meiner Hypothese, dass man den IN Operator nur für Skalare einsetzen könnte.

          Ich dachte kurz, man könnte das auch mit einem JOIN nachbilden, aber das führt (logischerweise) bei N Duplicates zu einer Ver-N-fachung der eindeutigen Sätze. Man müsste also noch einen DISTINCT nachschalten. Ich glaube nicht, dass das effizient ist.

          Was übrigens nicht geht, ist ein Vorfiltern der Duplikate auf diese Weise:

          DELETE FROM temp_table
          WHERE (feld2, feld3) IN (SELECT feld2, feld3
                                   FROM temp_table
                                   GROUP BY feld2, feld3
                                   HAVING COUNT(*) > 1)
          

          Grund: Ich darf in der WHERE Klausel eines DELETE nicht die Tabelle verwenden, aus der gelöscht wird. Wenn man die Duplikatsätze löschen will, müsste man eine Zwischentabelle haben, in der man die (feld2, feld3) Paare speichert, die zu löschen sind.

          Rolf

          --
          sumpsi - posui - clusi

          1. Today I Learned ↩︎

          1. Du kannst in der Tabellendefinition einen Unique-Index über die beiden Spalten setzen.

            Das hilft nicht, wenn die Daten Duplicates enthalten. Dann bricht der Import ab. Glaube ich...

            Man muss da nicht unbedingt erwas glauben, denn es kommt drauf an, wie man den Import macht...

          2. Hallo Rolf,

            Und damit wäre mein Ansatz dieser: eine Work-Table mit einen NON-UNIQUE Index, so dass Duplicates erstmal kein Problem sind, aber ein GROUP BY effizient via Index möglich ist. Und dann die Duplikate ausblenden. Das geht mit row-value-expressions und NOT IN.

            Haha...diese Tabelle existiert bereits 😉 Es ist tatsächlich so, dass ich eine temp_table habe, die ohne Unique-Index (mal vom Primary-Key abgesehen) läuft und aus der ohnehin die Daten 1:1 in die real_table-Tabelle kopiert werden sollen. Und genau das scheitert, wenn Feld2+Feld3 zweier Datensätze identisch sind, weil ich einen kombinierten Unique-Index auf Feld2+Feld3 liegen habe.

            Angenommen, die Tabelle, in die der wirkliche Import stattfinden soll, hieße real_table. Dann gelingt es mit folgender Magie:

            INSERT INTO real_table (feld1, feld2, feld3, feld4, feld5, feld6)
            SELECT feld1, feld2, feld3, feld4, feld5, feld6
            FROM temp_table
            WHERE (feld2, feld3) NOT IN (SELECT feld2, feld3
                                         FROM temp_table
                                         GROUP BY feld2, feld3
                                         HAVING COUNT(*) > 1)
            

            (feld2, feld3) ist eine row-value-expression. Sowas kannte ich bisher nur vom DB2 unseres Großrechners, und hielt es für eine nützliche, aber ansonsten unbekannte IBM Erweiterung. Weit gefehlt, das ist SQL-92 und es funktioniert z.B. auch in MYSQL 5.6.

            Das muß ich unbedingt ausprobieren... hört sich zu schön an, um wahr zu sein 😀

            Soviel zu meiner Hypothese, dass man den IN Operator nur für Skalare einsetzen könnte.

            Ich dachte kurz, man könnte das auch mit einem JOIN nachbilden, aber das führt (logischerweise) bei N Duplicates zu einer Ver-N-fachung der eindeutigen Sätze. Man müsste also noch einen DISTINCT nachschalten. Ich glaube nicht, dass das effizient ist.

            Was übrigens nicht geht, ist ein Vorfiltern der Duplikate auf diese Weise:

            DELETE FROM temp_table
            WHERE (feld2, feld3) IN (SELECT feld2, feld3
                                     FROM temp_table
                                     GROUP BY feld2, feld3
                                     HAVING COUNT(*) > 1)
            

            Grund: Ich darf in der WHERE Klausel eines DELETE nicht die Tabelle verwenden, aus der gelöscht wird. Wenn man die Duplikatsätze löschen will, müsste man eine Zwischentabelle haben, in der man die (feld2, feld3) Paare speichert, die zu löschen sind.

            Schade, aber einleuchtend. Aber wenn obige Query läuft (ich kanns erst morgen früh probieren), wäre ich sehr zufrieden.

            Auf jeden Fall Danke für den Tip!

            ...der war gut 😀

            Pit

            1. Hallo Pit,

              Und genau das scheitert, wenn Feld2+Feld3 zweier Datensätze identisch sind, weil ich einen kombinierten Unique-Index auf Feld2+Feld3 liegen habe

              Dann mach ihn doch Non Unique

              Rolf

              --
              sumpsi - posui - clusi
              1. Hallo Pit,

                Und genau das scheitert, wenn Feld2+Feld3 zweier Datensätze identisch sind, weil ich einen kombinierten Unique-Index auf Feld2+Feld3 liegen habe

                Dann mach ihn doch Non Unique

                Hallo Rolf,

                um Himmels Willen - nein. Es hat ja seinen grund, warum ich den Index gesetzt habe. Hintergrund ist, dass "in Echt" die Kombination aus Feld2 und Feld3 ein Produkt beschreibt, das eben tatsächlich "unique" ist. Trotzdem möchten dies sowohl User als auch meine Importlisten falscherweise unterlaufen. Das fange ich dann über einen Error 1062 ab. Insofern habe ich auch gar keine Hemmungen, beim Listenimport beide Zeilen heraus zu werfen, denn ich kann unmöglich wissen, welche der beiden hzeilen eigentlich die Korrekte wäre. Dies zu erruieren, wäre aber unwirtschaftlich, daher müssen beide (leider) raus.

                Pit

                1. Hallo Pit,

                  um Himmels Willen

                  Aber Herr Wöller!

                  Du schreibst, die Daten würden aus der Temp-Table 1:1 in die Real-Table übertragen. Mutmaßlich also mit einem INSERT. Und ich hatte dargestellt, wie Du bei diesem INSERT die Sätze ausblenden kannst, bei denen feld2+feld3 ein Duplikat enthalten. Reicht das nicht?

                  Wenn's mehr als ein INSERT ist - was ist es dann?

                  Rolf

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

                    Du schreibst, die Daten würden aus der Temp-Table 1:1 in die Real-Table übertragen. Mutmaßlich also mit einem INSERT. Und ich hatte dargestellt, wie Du bei diesem INSERT die Sätze ausblenden kannst, bei denen feld2+feld3 ein Duplikat enthalten. Reicht das nicht?

                    Doch klar. Ich hatte nur noch nicht die Zeit (bzw. den Rechner), es einzubauen und zu prüfen. Wenn das läuft, ist Deine Lösung ein Traum 😀

                    Morgen früh bin ich schlauer, ich melde mich ;-)

                    Pit

                    1. Hallo Pit,

                      Wenn das läuft, ist Deine Lösung ein Traum

                      Freut mich 😈

                      Rolf

                      --
                      sumpsi - posui - clusi
                      1. Hallo Pit,

                        Wenn das läuft, ist Deine Lösung ein Traum

                        Freut mich 😈

                        Hi Rolf,

                        hm...versteh ich jetzt nicht ganz...mach mich mal schlau, ok?

                        Pit

                        1. Hallo Pit,

                          das war mein Versuch, einen Scherz zu machen. Wenn jemand etwas als "einen Traum" bezeichnet, könnte es sich ja auch als Alptraum herausstellen.

                          Rolf

                          --
                          sumpsi - posui - clusi
                          1. Hallo Pit,

                            das war mein Versuch, einen Scherz zu machen. Wenn jemand etwas als "einen Traum" bezeichnet, könnte es sich ja auch als Alptraum herausstellen.

                            Hallo Rolf,

                            lol, Deine Erklärung dazu ist witziger als Dein Scherzversuch 😀

                            Nichts für Ungut 😉

                            Pit

                            1. Hallo Rolf,

                              nach ausführlichen Tests muß ich sagen, Deine Lösung funktioniert allerbestens! Finde ich ganz hervorragend! Danke!!!

                              Eine Zusatzfrage: Wenn ich mal irgendwann in die Situation käme (ist heut nicht der Fall), die ausgeschlossenen Datensätze extrahieren zu müssen, würde das gehen??

                              Gruß, Pit

                              1. Hallo Pit,

                                das scheint mir jetzt nicht schwierig zu sein; ich hätte gedacht, dass Du Dir diese Frage selbst beantworten kannst.

                                Entweder verstehe ich dein Problem bei der Sache nicht, oder Du müsstest mir sagen, was Du an dem Import-SQL nicht verstanden hast.

                                Rolf

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

                                  Entweder verstehe ich dein Problem bei der Sache nicht, oder Du müsstest mir sagen, was Du an dem Import-SQL nicht verstanden hast.

                                  Ich vermute, ich habe mich falsch ausgedrückt.

                                  Eine Zusatzfrage: Wenn ich mal irgendwann in die Situation käme (ist heut nicht der Fall), die ausgeschlossenen Datensätze extrahieren zu müssen, würde das gehen??

                                  Die Datensätze werden perfekt extrahiert. Die Zusatzfrage soll eher sein: Könnte man die extrahierten Datensätze aus der temp_table zusätzlich in eine temp_table2 kopieren (natürlich ohne die Datensätze, die in die real_table kopiert wurden)?

                                  Pit

                                  1. Die Zusatzfrage soll eher sein: Könnte man die extrahierten Datensätze aus der temp_table zusätzlich in eine temp_table2 kopieren (natürlich ohne die Datensätze, die in die real_table kopiert wurden)?

                                    Man könnte natürlich vorab folgende Query machen:

                                    INSERT INTO temp_table_extrahiert (feld1, feld2, feld3, feld4, feld5, feld6)
                                    SELECT feld1, feld2, feld3, feld4, feld5, feld6
                                    FROM temp_table
                                    WHERE (feld2, feld3) IN (SELECT feld2, feld3
                                                                 FROM temp_table
                                                                 GROUP BY feld2, feld3
                                                                 HAVING COUNT(*) > 1)
                                    

                                    und anschließend dann erst:

                                    INSERT INTO real_table (feld1, feld2, feld3, feld4, feld5, feld6)
                                    SELECT feld1, feld2, feld3, feld4, feld5, feld6
                                    FROM temp_table
                                    WHERE (feld2, feld3) NOT IN (SELECT feld2, feld3
                                                                 FROM temp_table
                                                                 GROUP BY feld2, feld3
                                                                 HAVING COUNT(*) > 1)
                                    

                                    Oder kann man diese Queries auch mit UNION verbinden?

                                    Pit

                                    1. Hallo Pit,

                                      genau so geht es.

                                      Und nein, eine „Ausgangsweiche“ gibt's in SQL nicht. Du kannst mit einem UNION zwei Datenquellen verbinden (sozusagen eine „Eingangsweiche“). Aber Du kannst eine Datenquelle nicht auf zwei Ziele aufteilen. Dazu musst Du, so wie jetzt besprochen, zwei Queries nacheinander laufen lassen, die je Ziel die jeweiligen Sätze selektieren.

                                      Es gibt Transformationstools, in denen Du sowas machen kannst. Das ist dann aber ein Addon zu MYSQL. Im Microsoft SQL Server, mit dem ich arbeite, gibt es SSIS (SQL Server Integration Services), mit denen man Dataflows dieser Art grafisch modellieren kann. Was es an freien oder kostenpflichtigen Produkten für MYSQL gibt, weiß ich nicht.

                                      Du kannst das natürlich auch in PHP schreiben. Dazu selektierst Du die temp-Tabelle sortiert nach feld2, feld3, liest Dich mit zweifachem Vorauslesen hindurch (um Duplikate zu finden) und schreibst je nach dem die Sätze nach links oder rechts. Das ist allerdings nicht ganz trivial und es ist nicht generisch, d.h. du passt das Script für jeden Sachverhalt neu an.

                                      Rolf

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

                                        genau so geht es.

                                        Prima.

                                        Und nein, eine „Ausgangsweiche“ gibt's in SQL nicht. Du kannst mit einem UNION zwei Datenquellen verbinden (sozusagen eine „Eingangsweiche“). Aber Du kannst eine Datenquelle nicht auf zwei Ziele aufteilen. Dazu musst Du, so wie jetzt besprochen, zwei Queries nacheinander laufen lassen, die je Ziel die jeweiligen Sätze selektieren.

                                        Ja, habe ich auch schon gemerkt. Ich habe es mit UNION getestet, aber natürlich entsprechend Deiner Erklärung einen Fehler erhalten.

                                        Es gibt Transformationstools...

                                        Muß nicht. Ich kann gut damit leben, 2 Queries abzusetzen.

                                        Aber ich hätte noch eine Frage, und zwar habe ich gemerkt, dass viele Datensätze aussortiert werden, die das auch verdient haben 😉 Aber bei dieser Durchsicht fiel mir auf, dass es ja schön wäre, wenn von den Datensätzen, bei denen wirklich alle Felder identisch sind, doch 1 Datensatz in die real_table kopiert werden würde.

                                        Hast Du eine Idee?

                                        Gruß, Pit

                                        1. Hallo Pit,

                                          am einfachsten wäre es wohl, wenn Du die extrahierten Sätze mit SELECT DISTINCT in temp_table_extrahiert einfügst. Die, die komplett identisch waren, stehen dann nur einmal in temp_table_extrahiert.

                                          Wenn Du nun die NOT IN Query noch einmal laufen lässt, mit temp_table_extrahiert an Stelle von temp_table als Quelle (an beiden Stellen!), sollten diese Sätze ebenfalls übernommen werden.

                                          Dann sind es 3 Queries, und es ist ein ziemliches Gefummel, ich weiß.

                                          Und die vierte Query sowie eine weitere Table folgen dann für den Extrakt des Extraktes, damit Du die Sätze hast, die WIRKLICH nicht importiert wurden.

                                          Wenn Du keine weitere Table willst, müsstest Du temp_table putzen und temp_table_extrahiert dorthin kopieren, bevor Du den zweiten Lauf machst. Dann musst Du auch die Query nicht anpassen ;)

                                          Rolf

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

                                            ich habe gerade nochmal nachgesehen. Auch die Daten für die temp_table kommen aus einem Array, d.h. ich kann schon im php array_unique anwenden.😏

                                            Kommt letztlich auf dasselbe raus, ich hatte nur nicht daran gedacht...

                                            Gruß, Pit

                                            1. Hallo Pit,

                                              äh wie? Du schreibst die Daten selbst mit PHP?

                                              Aber warum, zum grundgütigen Geier, dann die Umstände? Dann kannst Du doch auch die Duplikate bei feld2/feld3 eliminieren, bevor Du die CSV Datei schreibst.

                                              Das ist natürlich nicht ganz einfach, und der Algorithmus hängt auch davon ab, ob es außer Duplikaten auch drei oder mehr Sätze mit gleichen Werten für feld2/feld3 geben kann.

                                              Rolf

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

                                                äh wie? Du schreibst die Daten selbst mit PHP?

                                                Nein, ich schreibe sie nicht selber. Ich erhalte eine fertige CSV-Datei, die ich in php einlese. Sie liegen dann eben nur bereits in einem array vor, genauer gesagt, in einem assoziativem Array.

                                                Array
                                                (
                                                    [0] => Array
                                                        (
                                                            [0] => Feld 1
                                                            [1] => Feld 2
                                                            [2] => Feld 3
                                                            [3] => Feld 4
                                                        )
                                                    [1] => Array
                                                        (
                                                            [0] => Feld 1
                                                            [1] => Feld 2
                                                            [2] => Feld 3
                                                            [3] => Feld 4
                                                        )
                                                usw.
                                                
                                                )
                                                

                                                Aber warum, zum grundgütigen Geier, dann die Umstände? Dann kannst Du doch auch die Duplikate bei feld2/feld3 eliminieren, bevor Du die CSV Datei schreibst.

                                                Und ab hier trage ich in die DB ein, nämlich in temp_table.

                                                Das ist natürlich nicht ganz einfach, und der Algorithmus hängt auch davon ab, ob es außer Duplikaten auch drei oder mehr Sätze mit gleichen Werten für feld2/feld3 geben kann.

                                                Klar, sowas kommt (leider) vor.

                                                Ich eliminiere jetzt die wirklich gleichen Zeilen in der temp_table so:

                                                INSERT INTO TABLE_temp2
                                                SELECT DISTINCT * FROM TABLE_temp;
                                                DELETE FROM TABLE_temp;
                                                INSERT INTO TABLE_temp
                                                SELECT * FROM TABLE_temp2
                                                DELETE FROM TABLE_temp2;
                                                

                                                Ein unnötiger Schritt dabei, ich weiß. ich könnte auch von der TABLE2 in die real_table kopieren.

                                                Aber wenn ich Dich richtig verstehe, würdest Du inzwischen alles über php machen?

                                                Pit

                                            2. Hallo Pit,

                                              ich habe mal experimentiert - wenn Du die Daten bereits als Array vorliegen hast, ist es am einfachsten, wenn Du sie in eine hierarchische Darstellung überführst.

                                              Damit meine ich ein Array, in dem die Werte von feld2 der Key sind. Jeder Eintrag davon ist ein Array, in dem die Werte von feld3 der Key sind. Und davon ist jeder Eintrag ein Array mit den Zeilen, die diese Wertekombination für feld2 und feld3 haben.

                                              Diese Arrayhierarchie durchläufst Du dann mit foreach und kannst auf jedes Zeilen-Array einen array_unique anwenden. Ist der count für dieses Array dann 1, schreibst Du dafür den CSV Satz. Oder machst sofort den Insert in die real_table und sparst Dir das CSV Geraffel.

                                              Mal angenommen, alle Sätze stehen in einem Array $data.

                                              $data = ARRAY(
                                                  [ "feld1" =>  1, "feld2" => 7, "feld3" => 8, "feld4" => "Mumpf" ],
                                                  [ "feld1" => 17, "feld2" => 4, "feld3" => 9, "feld4" => "Foo" ],
                                                  [ "feld1" => 17, "feld2" => 4, "feld3" => 9, "feld4" => "Foo" ],
                                                  [ "feld1" => 45, "feld2" => 5, "feld3" => 2, "feld4" => "Dings" ],
                                                  [ "feld1" => 46, "feld2" => 5, "feld3" => 2, "feld4" => "Pings" ],
                                              );
                                              

                                              Das überführst Du in die genannte Hierarchie. Statt foreach ($data as $row) kann es auch eine while-Schleife sein, die ein SQL Query-Result einliest.

                                              $rowDict = [ ];
                                              
                                              foreach ($data as $row) {
                                                 $f2 = $row["feld2"];    // Variablen für Lesbarkeit
                                                 $f3 = $row["feld3"];
                                              
                                                 if (empty($rowDict[$f2])) {
                                                    $rowDict[$f2] = [ $f3 => [ $row ] ];
                                                 }   
                                                 else if (empty($rowDict[$f2][$f3])) {
                                                    $rowDict[$f2][$f3] = [ $row ];
                                                 }   
                                                 else {
                                                    $rowDict[$f2][$f3][] = $row;
                                                 }   
                                              }
                                              

                                              Die Schleife prüft Stufe für Stufe der Hierarchie, ob es für diese Stufe schon einen Eintrag gibt. Wenn nicht, wird der Eintrag auf dieser Stufe angelegt. Wenn doch, wird die Stufe entsprechend ergänzt.

                                              Das kannst Du nun verarbeiten:

                                              foreach ($rowDict as $f2Dict) {
                                                 foreach ($f2Dict as $rows) {
                                                    $uniqRows = array_unique($rows, SORT_REGULAR);
                                                    if (count($uniqRows) == 1)
                                                       importToDatabase($uniqRows[0]);
                                                    else
                                                       writeDuplicates($uniqRows);
                                                 }
                                              }
                                              

                                              importToDatabase kann den CSV Satz schreiben, oder direkt den INSERT machen. Und writeDuplicates kann eine CSV-Datei mit den Duplikaten schreiben, oder sie in eine Tabelle mit den Duplikaten INSERTen. Ganz wie Du willst.

                                              Rolf

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

                                                ich habe mal experimentiert - wenn Du die Daten bereits als Array vorliegen hast, ist es am einfachsten, wenn Du sie in eine hierarchische Darstellung überführst.

                                                Damit meine ich ein Array, in dem die Werte von feld2 der Key sind. Jeder Eintrag davon ist ein Array, in dem die Werte von feld3 der Key sind. Und davon ist jeder Eintrag ein Array mit den Zeilen, die diese Wertekombination für feld2 und feld3 haben.

                                                Oweia, jetzt wirds mir zu kompliziert 😉 Ich fand die sql-Lösung sehr sympatisch. Zudem muß ich im php-Teil noch jede Menge Felder bearbeiten, ergänzen, tauschen, usw. Da das wirklich seit langem sehr sauber läuft, würde ich eigentlich da gerne nicht mehr Hand anlegen. Insofern kam und kommt mir die sql-Lösung eigentlich sehr entgegen.

                                                Würde Deine php-Lösung eigentlich sehr viel einfacher, wenn anstelle von Feld[2] und Feld[3] lediglich auf eines der beiden Felder geprüft werden müßte? Ich frage nur, weil ich glaiube, dass auch das ausreichen könnte, wenn ich einen anderen teil umprogrammiere.

                                                Pit

                                                1. Hallo Pit,

                                                  nein, nicht sehr viel. Die Array-Hierarchie sinkt um eine Stufe, d.h. die Abfragen beim Aufbau werden etwas einfacher, und beim Auslesen ist ein foreach weniger.

                                                  D.h. statt 23 Codezeilen sind's dann 18 oder so.

                                                  Das unsympathische an der SQL Lösung ist für mich, dass Du Daten erstmal rausschreibst, um sie extern zu überarbeiten. Dann importierst Du sie wieder. Es stellt sich nun heraus, dass die Überarbeitung eigentlich ganz einfach ist, man muss nur das Problem lösen, die Sätze mit gleichem Key zusammen zu bekommen.

                                                  Dafür gibt's auch andere Möglichkeiten wie eine Array-Hierarchie, aber die sind eher komplizierter.

                                                  Nur Mut! 😉

                                                  Das $rowDict Array würde für meine Beispieldaten von oben so aussehen (ich zeige für die Rows nur feld4, sonst wird's für das Forum zu breit). Für die Keys 4, 9 würde array_unique eine Identität erkennen und der Satz würde importiert werden.

                                                  $rowDict = ARRAY(
                                                     4 => ARRAY(
                                                             9 => ARRAY(
                                                                      ARRAY(.... "feld4" => "Foo"),
                                                                      ARRAY(.... "feld4" => "Foo")
                                                                  )
                                                          ),
                                                     5 => ARRAY(
                                                             2 => ARRAY(
                                                                      ARRAY(.... "feld4" => "Dings"),
                                                                      ARRAY(.... "feld4" => "Pings")
                                                                  )
                                                          ),
                                                     7 => ARRAY(
                                                             8 => ARRAY(
                                                                      ARRAY(.... "feld4" => "Mumpf"),
                                                                  )
                                                          )
                                                  );
                                                  

                                                  Lässt Du feld2 oder feld3 weg, hat diese Hierarchie eine Stufe weniger.

                                                  Sind feld2 und feld3 beides Strings, könntest Du auch einen kombinierten Schlüssel machen, sowas wie $feld2."~".$feld3, und den als Key für das Dictionary nutzen. Dann hast Du eine Hierarchiestufe weniger, aber kannst trotzdem beide Schlüssel verwenden. Der Aufbau des Dictionary sieht dann so aus (da Du ein Freund von ARRAY() statt [] zu sein scheinst, habe ich das mal verwendet):

                                                  $rowDict = ARRAY();
                                                  
                                                  foreach ($data as $row) {
                                                     $key = $row["feld2"] . "~" . $row["feld3"];  // Schlüssel der Row
                                                  
                                                     // Noch kein Eintrag zum $key -> Neues Subarray anlegen
                                                     // Andernfalls an's vorhandene Subarray anhängen.
                                                     if (empty($rowDict[$key])) {
                                                        $rowDict[$key] = ARRAY( $key => $row);
                                                     }   
                                                     else {
                                                        $rowDict[$key][] = $row;
                                                     }   
                                                  }
                                                  

                                                  Bei der Auswertung entfällt dann eine der geschachtelten foreach-Schleifen.

                                                  Rolf

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

                                                    ich habe das jetzt mal testhalber umgesetzt und es läuft auch. Trotzdem ist mir die SQL-Lösung in Summe gesehen sympatischer, was vielleicht auch daran liegehn mag, dass ich den SQL-Ansatz gleich in 2 verschiedene Teile meines Programms habe einsetzen können.

                                                    Zudem kann ich die reine Query auch nativ im phpmyadmin imm er dort nutzen, wo ich nachträglich einen unique Index setzen möchte. Alles in allem also für mich ein sehr hilfreiches Code-Snippet. (Hierfür und überhaupt für Deine Mühe nochmal herzlichen Dank!).

                                                    Ich habe aber noch eine Frage, die mich gerade beschäftigt und in das Thema passt, nämlich würde ich gerne in meinem Array, in dem die Zeilen der CSV-Datei das Array bilden, für jede Zeile nachzählen, ob die Anzahl der Delimiter gleich ist. Das hört sich erstmal einfacher an, als es ist, wie ich finde.

                                                        $myCsvArray = explode("\n",file_get_contents($csv_datei));
                                                        foreach ($myCsvArray AS $myEinzelZeile) {
                                                            $myCount = substr_count($myEinzelZeile, '|');
                                                        }
                                                    

                                                    Es fängt damit an, schwierig zu werden, dass ich keinen Anhaltswert für die "passende" Anzahl an Delimitern habe. Diese würde ich auch gerne nicht manuell vorgeben, sprich, ich müßte sie aus meinem Array erstmal ableiten. Hilfreich hierbei ist zu wissen, dass 99% der Zeilen die passende Anzahl an Delimitern vorweisen. Frage also, wie spucke ich mir die Zeilen aus, die ggf. zu wenige Delimiter haben, aus??

                                                    Meine Idee wäre, erstmal die ersten 100 Zeilen zu durchlaufen und dadurch die "passende Anzahl" an Delimitern festzulegen. Anschließend würde ich dann neu anfangen und alle Zeilen durchlaufen.

                                                    Hast Du (oder natürlich auch jeder andere) eine bessere Idee?

                                                    Pit

                                                    1. Meine Idee wäre, erstmal die ersten 100 Zeilen zu durchlaufen und dadurch die "passende Anzahl" an Delimitern festzulegen. Anschließend würde ich dann neu anfangen und alle Zeilen durchlaufen.

                                                          // Zählen, ob Anzahl der Delimiter je Zeile identisch ist
                                                          $myCsvArray = explode("\n",file_get_contents($csv_datei));
                                                          $zahl5 = $maxCount = 0;
                                                          foreach($myCsvArray AS $myEinzelZeile) {
                                                              $zahl5++;
                                                              $myCount = substr_count($myEinzelZeile,'|');
                                                              if($myCount > $maxCount) {
                                                                  $maxCount = $myCount;
                                                              }
                                                              if($zahl5 == 100) {
                                                                  break;
                                                              }
                                                          }
                                                      
                                                          $myErforderlicheDelimiter = $maxCount;
                                                          $myZeile = 0;
                                                      
                                                          foreach($myCsvArray AS $myEinzelZeile) {
                                                              $myZeile++;
                                                              if(substr_count($myEinzelZeile,'|') != $myErforderlicheDelimiter) {
                                                                  echo("Fehlerhafte Anzahl Delimiter in Zeile ".$myZeile."");
                                                              }
                                                          }
                                                      
                                                      

                                                      Hast Du (oder natürlich auch jeder andere) eine bessere Idee?

                                                      Pit

                                                      1. $myCount = substr_count($myEinzelZeile,'|');
                                                        

                                                        Ist denn in Deiner Definition von CSV eventuell vorgesehen oder ausgeschlossen, dass das als Trenner verwendete PIPE-Symbol in den Werten selbst vorkommt und wie wird es im ersten Fall maskiert?

                                                        (Pit:) Es fängt damit an, schwierig zu werden, dass ich keinen Anhaltswert für die "passende" Anzahl an Delimitern habe.

                                                        Den gäbe es wohl, wenn Du endlich damit herausrücken würdest wie das CSV denn erzeugt wird.

                                                        1. Hi,

                                                          Ist denn in Deiner Definition von CSV eventuell vorgesehen oder ausgeschlossen, dass das als Trenner verwendete PIPE-Symbol in den Werten selbst vorkommt und wie wird es im ersten Fall maskiert?

                                                          Pipe kommt nicht vor. Falls doch, ersetze ich sie durch etwas anderes, bevor ich überhaupt anfange.

                                                          (Pit:) Es fängt damit an, schwierig zu werden, dass ich keinen Anhaltswert für die "passende" Anzahl an Delimitern habe.

                                                          Den gäbe es wohl, wenn Du endlich damit herausrücken würdest wie das CSV denn erzeugt wird.

                                                          Ich bekomme fertige Excellisten, die ich in csv-Dateien überführe. Soll heißen, ich habe nahezu keinen Einfluß auf die csv, wenn ich mal vom Delimiter oder dem Feldtrenner absehe.

                                                          Pit

                                                          1. Ich bekomme fertige Excellisten, die ich in csv-Dateien überführe.

                                                            Warum machst Du es Dir dann schwer?

                                                            1. Ich bekomme fertige Excellisten, die ich in csv-Dateien überführe.

                                                              Warum machst Du es Dir dann schwer?

                                                              Aber die Art, wie ich die Datei einlese, beantwortet doch meine Frage nicht?

                                                              Oder möchtest Du mir noch erklären, was genau Du mir sagen willst?

                                                              fgetcsv() kenne und nutze ich. Es geht aber doch jetzt darum, die korrekte Anzahl der Delimiter für die jeweilige Datei zu ermitteln und anschließend event. fehlerhafte Zeilen auszugeben. Das klappt mit miener obigen Lösung sehr gut. Was würde also fgetcsv() hieran verbessern?

                                                              Pit

                                                            2. Hallo Raketenwissenschaftler,

                                                              TIL: CSV in PHP 😀

                                                              Es gibt auch noch str_getcsv, falls man die Zeile bereits in einem String hat.

                                                              Aber sicherlich ist es effizienter (speichermäßig), die Datei mit fopen zu öffnen und mit fgetcsv zu lesen (auf der fgetcsv-Seite von php.net ist auch ein Code-Snippet dafür).

                                                              Und dann würde ich die Einträge in dem Array zählen, das fgetcsv liefert, und in einem Array zählen, welche Länge wie oft vorkommt. Sowas wie

                                                              $haeufigkeit = ARRAY();
                                                              $anz_zeilen = 0;
                                                              while (($data = fgetcsv($handle, 0, ",")) !== FALSE) {
                                                                 $anz_felder = count($data);
                                                                 $haeufigkeit[$anz_felder] = empty($haeufigkeit[$anz_felder])
                                                                                                 ? 1
                                                                                                 : ($haeufigkeit[$anz_felder] + 1);
                                                                 $anz_zeilen++;
                                                                 // weitere Verarbeitung
                                                              }
                                                              

                                                              und dann musst Du Statistik machen und schauen, ob für einen der gefundenen Häufigkeitszähler die Quote $haeufigkeit[$i] / $anz_zeilen größer als 0.99 ist.

                                                              Aber eigentlich müsstest Du doch eine bestimmte Anzahl von Feldern erwarten, oder?

                                                              Rolf

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

                                                                Aber eigentlich müsstest Du doch eine bestimmte Anzahl von Feldern erwarten, oder?

                                                                Selbstverständlich weiß ich das. Aber ich finde es zum lernen viel spaßiger, es zu ermitteln. Und natürlich ist es auch (zwar nicht viel, aber immerhin) weniger Arbeit, wenn das Script das ermitteln übernimmt, zumal sich die Anzahl der Delimiter von csv zu csv ändert.

                                                                Frage: Was ist denn effizienter?

                                                                $myCsvArray = explode("\n",file_get_contents($csv_datei));
                                                                foreach($myCsvArray AS $myEinzelZeile) {
                                                                ...
                                                                }
                                                                
                                                                oder:
                                                                
                                                                $handle = fopen($csv_datei, "r");
                                                                while (($data = fgetcsv($handle)) !== FALSE) {
                                                                
                                                                ...
                                                                }
                                                                

                                                                Pit

                                                                1. Hallo Pit,

                                                                  ich finde es zum lernen viel spaßiger

                                                                  Lernst Du? Oder lieferst Du ein Produkt an einen Kunden ab? Abgesehen davon: Wenn 10 Spalten erwartet werden, aber 99% der Zeilen haben 9 Spalten, was sagt das über die Datei? Ist sie falsch? Ist das normal? Solche Fragen musst Du beantworten können. Du erwartest ja bestimmte Daten, die Du in eine konkrete Tabelle importieren willst. Wenn es zu wenig Spalten sind, dann fehlt was. Was tust Du mit der Lücke?

                                                                  Was ist denn effizienter?

                                                                  Falsche Frage. Ok, nicht uninteressant, aber nicht entscheidend.

                                                                  Korrekte Frage: Was ist effektiver[1]? Du wirst das ja nicht alle 5 Minuten laufen lassen, darum ist Effizienz nachrangig. fgetcsv kümmert sich nicht nur um das Separieren derFelder in ein Array, sondern auch darum, delimited strings richtig zu behandeln (wenn z.B. der Feldseparator ein Zeichen in den Daten ist

                                                                  1;47.11;"Ein String; aber was für einer";1234
                                                                  

                                                                  fgetcsv sollte das korrekt behandeln. Ein eigener explode(";", $zeile) macht es falsch.

                                                                  Und ich denke, dass die fgetcsv Lösung auch effizienter ist. Sie liest die Datei Zeile für Zeile, so dass Du nicht die ganze Datei ins RAM saugen musst. Darüber hinaus sollte sie in C implementiert sein, d.h. sollte auch weniger Laufzeit als eine PHP Lösung brauchen.

                                                                  Rolf

                                                                  --
                                                                  sumpsi - posui - clusi

                                                                  1. effizient: schneller, speichersparender. effektiv: führt die geforderte Funktion korrekt aus ↩︎

                                                                  1. Hallo Rolf B,

                                                                    Korrekte Frage: Was ist effektiver?

                                                                    effizient: schneller, speichersparender. effektiv: führt die geforderte Funktion korrekt aus

                                                                    Eben. Deshalb gibt es kein effektiver (siehe auch wiktionary/effektiv).

                                                                    Bis demnächst
                                                                    Matthias

                                                                    --
                                                                    Du kannst das Projekt SELFHTML unterstützen,
                                                                    indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
                                                                    1. Hallo,

                                                                      Eben. Deshalb gibt es kein effektiver (siehe auch wiktionary/effektiv).

                                                                      Und wenn nun deine Frau schwangerer ist als meine?

                                                                      Gruß
                                                                      Kalk

                                                                      1. Hi,

                                                                        Und wenn nun deine Frau schwangerer ist als meine?

                                                                        dann bekommt sie Zwillinge und Deine Frau nur ein Kind.
                                                                        Oder Drillinge und Zwillinge.
                                                                        Oder Vierlinge und Drillinge.
                                                                        Oder …

                                                                        cu,
                                                                        Andreas a/k/a MudGuard

                                                                    2. Hallo Matthias,

                                                                      Deshalb gibt es kein effektiver

                                                                      und das sagst Du mir zum einzigsten Mal, ich weiß.

                                                                      Für Software ist effektiv aber kein schwarz-weiß. Zum einen wegen der 80-20 Regel, zum anderen, weil jede nichttriviale Software mindestens einen Bug enthält.

                                                                      Rolf

                                                                      --
                                                                      sumpsi - posui - clusi
                                                                  2. Hallo Rolf,

                                                                    ich finde es zum lernen viel spaßiger

                                                                    Lernst Du? Oder lieferst Du ein Produkt an einen Kunden ab?

                                                                    In diesem Fall lerne ich. Worum es hier geht, ist alleine für mich gescriptet und ich möchte darin lernen und daran wachsen.

                                                                    Abgesehen davon: Wenn 10 Spalten erwartet werden, aber 99% der Zeilen haben 9 Spalten, was sagt das über die Datei? Ist sie falsch?

                                                                    Halt stop. Das passiert nicht, denn ich überprüfe vorab, ob ein Spaltentext meinen Delimiter enthält. Deshalb können nicht mehr Delimiter erwartet werden als der zuvor ermittelte Max-Weret ist. Und selbst wenn, würden mir unerwartet viele fehler gemeldet, was mich auch dazu zwingen würde, mir die daten genauer anzusehen.

                                                                    Was ist denn effizienter?

                                                                    Falsche Frage. Ok, nicht uninteressant, aber nicht entscheidend.

                                                                    Korrekte Frage: Was ist effektiver[^1]? Du wirst das ja nicht alle 5 Minuten laufen lassen, darum ist Effizienz nachrangig. fgetcsv kümmert sich nicht nur um das Separieren derFelder in ein Array, sondern auch darum, delimited strings richtig zu behandeln (wenn z.B. der Feldseparator ein Zeichen in den Daten ist

                                                                    Auch wenn ich bereits Mathias' Antwort darauf gelesen habe, kann ich Deiunem Einwand etwas abgewinnen, egal wie wir das benennen. Du hast recht: Was ich meine, ist tatsächlich die Frage danach, welche Lösung im Fale des falles (sehr sehr viele Daten) noch durchläuft, nihct die Frage danach, welche Lösung das schneller macht.

                                                                    Pit

                                                          2. Lieber Pit,

                                                            Ich bekomme fertige Excellisten, die ich in csv-Dateien überführe.

                                                            ab jetzt wirkt die Sache auf mich doch reichlich seltsam. DU erzeugst die CSV-Dateien. DU hast die Kontrolle über die Anzahl Spalten. DU willst das Pipe-Symbol als Trenner nutzen.

                                                            Warum soll dann PHP flexibel auf Deine CSV-Dateien reagieren?

                                                            Warum wertet PHP Deine Excel-Dateien nicht direkt aus? PHP-Office existiert.

                                                            Liebe Grüße

                                                            Felix Riesterer

                                                            1. Lieber Pit,

                                                              Ich bekomme fertige Excellisten, die ich in csv-Dateien überführe.

                                                              ab jetzt wirkt die Sache auf mich doch reichlich seltsam. DU erzeugst die CSV-Dateien. DU hast die Kontrolle über die Anzahl Spalten. DU willst das Pipe-Symbol als Trenner nutzen.

                                                              Klar, auf den Delimiter habe ich Einfluss, aber doch nicht auf die Anzahl der Spalten?!

                                                              Warum soll dann PHP flexibel auf Deine CSV-Dateien reagieren?

                                                              Nicht PHP, sondern mein php-Script.

                                                              Warum wertet PHP Deine Excel-Dateien nicht direkt aus? PHP-Office existiert.

                                                              Kannst Du da mal bitte genauer werden? Was genau ist das und was kann ich damit machen? Zumindest klingt es interessant.

                                                              Grüße, Pit

                                                              1. Lieber Pit,

                                                                Klar, auf den Delimiter habe ich Einfluss, aber doch nicht auf die Anzahl der Spalten?!

                                                                Du hast absoluten (lies: uneingeschränkten) Einfluss auf die Excel-Datei. Klar kannst Du dort die Anzahl der Spalten festlegen.

                                                                Nicht PHP, sondern mein php-Script.

                                                                Da habe ich das Acronym in diesem Sinne verwendet. PHP selbst kann erst etwas tun, wenn ich ein Script schreibe, das die Möglichkeiten von PHP nutzt.

                                                                Warum wertet PHP Deine Excel-Dateien nicht direkt aus? PHP-Office existiert.

                                                                Kannst Du da mal bitte genauer werden? Was genau ist das und was kann ich damit machen?

                                                                Es ist eine PHP-Scripte-Sammlung, um damit Office-Dateien (MS-Office-Formate und auch OpenDocument wie es LibreOffice verwendet) zu lesen und zu schreiben.

                                                                Zumindest klingt es interessant.

                                                                Du könntest Dir damit das ganze Geraffel mit den CSV-Dateien sparen.

                                                                Wenn man PhpSpreadsheet aus der Sammlung einsetzt, kann man damit Inhalte von Excel-Dateien auswerten. Ich habe mit dem Vorgänger Excel-Dateien dynamisch erstellt und zum Download angeboten, sowie hochgeladene Excel-Dateien ausgelesen und deren Inhalte weiter verarbeitet.

                                                                Liebe Grüße

                                                                Felix Riesterer

                                                                1. Hallo Felix,

                                                                  Klar, auf den Delimiter habe ich Einfluss, aber doch nicht auf die Anzahl der Spalten?!

                                                                  Du hast absoluten (lies: uneingeschränkten) Einfluss auf die Excel-Datei. Klar kannst Du dort die Anzahl der Spalten festlegen.

                                                                  Nein, das kann ich nicht. Es sei denn, ich möchte auf Informationen aus den Dateien verzichten. Mein Script arbeitet aber so, dass ich alle Informationen der Datei aufnehmen kann, unabhängig davon, wieviele Spalten die Ursprungsdatei hat. Kürze ich aber die Anzahl der Spalten, gebe ich Informationen weg...und zwar ins digitale Nirvana.

                                                                  Du könntest Dir damit das ganze Geraffel mit den CSV-Dateien sparen.

                                                                  Wenn man PhpSpreadsheet aus der Sammlung einsetzt, kann man damit Inhalte von Excel-Dateien auswerten. Ich habe mit dem Vorgänger Excel-Dateien dynamisch erstellt und zum Download angeboten, sowie hochgeladene Excel-Dateien ausgelesen und deren Inhalte weiter verarbeitet.

                                                                  Das finde ich sehr interesant. Habe ich zwar augenblicklich keine verwendung für (auch wenn es so ausschaut), aber ich guck mir das trotzdem mal an. Leider sind die aktuellen Dateien, die ich verarbeite, teilweise sowas von unsinnig erstellt, dass ich immer per Hand ran muß, bevor ich mein Script daruf ansetzen kann.

                                                                  Pit

                                                                  1. Mein Script arbeitet aber so, dass ich alle Informationen der Datei aufnehmen kann, unabhängig davon, wieviele Spalten die Ursprungsdatei hat.

                                                                    Scheinbar bist Du an einem Punkt, an welchem das realtionale Datenbankmodell nicht mehr passt.

                                                                    • "Schema SQL databases have a predefined schema"
                                                                    • "NoSQL databases use dynamic schema for unstructured data."

                                                                    Wenn Deine "Datensätze" in Deiner Datei nicht die gleiche Anzahl an Items haben, dann haben diese jeweils eine unterschiedliche, insgesamt ergo keine definierbare Struktur.

                                                                    Es sei denn natürlich, Du hast in ein Programm umsetzbare Anhaltspunkte (Definitionen) dafür, wie Du Deine kaputte Struktur (unterschiedliche Anzahl an Datensätzen) in eine definierte Struktur für eine SQL-Datenbank umwandeln willst.

                                                                    Dafür gibt es aber keine allgemeinen Regeln, die kann man nur für den speziellen Anwendungsfall bauen. Von Deinem speziellen Anwendungsfall weiß ich aber nicht genügend als dass ich effektiv helfen könnte. Anderen gehts auch so: Das ist hier "Stochern im Nebel".

                                                                    1. Es sei denn natürlich, Du hast in ein Programm umsetzbare Anhaltspunkte (Definitionen) dafür, wie Du Deine kaputte Struktur (unterschiedliche Anzahl an Datensätzen) in eine definierte Struktur für eine SQL-Datenbank umwandeln willst.

                                                                      Muss heißen:

                                                                      Es sei denn natürlich, Du hast in ein Programm umsetzbare Definitionen dafür, wie Du Deine kaputte Struktur (unterschiedliche Anzahl an Items in den Zeilen) in eine definierte Struktur für eine SQL-Datenbank umwandeln willst.

    2. Sowas?

      Felds|Feld2|Feld3|Feldv|Feldg
      Feldg|Feld2|Feld3|Feldc|Feldk
      

      Shell-Variante:

      sort -u -t'|' -k 2,3 < original.csv > unique.csv
      

      Ergebnis:

      Felds|Feld2|Feld3|Feldv|Feldg
      
      1. Hallo Raketenwissenschaftler,

        Shell-Variante:

        sort -u -t'|' -k 2,3 < original.csv > unique.csv
        

        Danke dafür, aber eine Shell-Variante komt eher nicht in Frage. 😟

        Pit

        1. Danke dafür, aber eine Shell-Variante komt eher nicht in Frage

          Wenn es sich wirklich um große Datenmengen handelt dann sollte statt "eher nicht" ein guter Grund da stehen.

          Du kannst das Shell-Zeug auch in ein Skript schreiben, dieses dann in PHP kapseln (eg. "deinskript.sh"), die Fehlerausgaben darin mit 2>logfile umleiten und die Exit-Codes jeweils aus $? auslesen...

      2. Und wenn wir schon dabei sind, kannst Du Dir auch den Umweg über PHP ganz sparen:

        Die Hilfe zu mysql_import() sagt:

        --fields-enclosed-by
            This option has the same meaning as the
            corresponding clause for LOAD DATA 		
        --fields-escaped-by
            This option has the same meaning as the
            corresponding clause for LOAD DATA 		
        --fields-optionally-enclosed-by
            This option has the same meaning as the
            corresponding clause for LOAD DATA 		
        --fields-terminated-by
            This option has the same meaning as the
            corresponding clause for LOAD DATA 		
        --force
            Continue even if an SQL error occurs
        
    3. Lieber Pit,

      Oft sind es gar keine echten Duplikate, die mich in den csv-Dateien stören. Denn für meine Nöte sind eigendlich nur 2 Felder der csv-Datei zuständig, also hier zum Beispiel Feld2 und Feld3.

      dann bietet sich ein assoziatives Array an, bei dem der Index aus den Werten der Felder 2 und 3 zusammengesetzt ist. Sollte es also mehrfache Kombinationen derselben Werte geben, wird der Array-Eintrag jedes Mal überschrieben, wenn er schon existieren sollte. Damit ist sichergestellt, dass er nur einmal vorkommt:

      $unique = array();
      
      foreach ($zeile as $felder) {
        // Feld2 entspricht $felder[1] und Feld3 $felder[2]
        $key = sprintf('%s_%s', $felder[1], $felder[2]);
      
        $unique[$key] = array(
          'Feld2' => $felder[1],
          'Feld3' => $felder[2]
        );
      }
      

      Liebe Grüße

      Felix Riesterer

      1. Hallo Felix,

        dann bietet sich ein assoziatives Array an, bei dem der Index aus den Werten der Felder 2 und 3 zusammengesetzt ist. Sollte es also mehrfache Kombinationen derselben Werte geben, wird der Array-Eintrag jedes Mal überschrieben, wenn er schon existieren sollte. Damit ist sichergestellt, dass er nur einmal vorkommt:

        Ja, würde gehen. Entspricht dann aber nicht mehr ganz meinem Vorhaben, beide Einträge heraus zu werfen, der erste würde immer "gewinnen" und bleiben.

        Gruß und danke für die Idee,

        Pit

        1. Hallo,

          Entspricht dann aber nicht mehr ganz meinem Vorhaben, beide Einträge heraus zu werfen, der erste würde immer "gewinnen" und bleiben.

          Nicht wenn du da ein

          If (isset) unset
          

          unterbringen kannst

          Gruß
          Kalk

          1. Entspricht dann aber nicht mehr ganz meinem Vorhaben

            Ich bin aber schon ein wenig "angefressen" weil Du Dein Vorhaben nicht gleich richtig beschreibst sondern erst nach dem Vorstellen von Lösungsmöglichkeiten schrittweise mit "Aber ich will …" nachträgst. Und zwar jeden Punkt schön einzeln.

            Das Vorhaben von Anfang an richtig und umfassend zu beschreiben (z.B. mit "Programmablaufplan") führt zielgerichtet zu richtigen und Dir nützenden Antworten.

            So wie Du Dein Vorhaben beschreibst bekommst Du viele Antworten, die zwar richtig sind, Dir aber nichts nützen.

            1. Hallo,

              nicht mehr ganz meinem Vorhaben

              [...] Dein Vorhaben [...] viele Antworten, [...] nichts nützen.

              und wenn dann noch die Antwortenden mit ihren Antworten auf andere Antwortende antworten, aber eigentlich anderen antworten wollten, erreichen wir eine ziemlich gute Annäherung ans Chaos…

              Gruß
              Kalk

        2. Lieber Pit,

          Ja, würde gehen. Entspricht dann aber nicht mehr ganz meinem Vorhaben, beide Einträge heraus zu werfen, der erste würde immer "gewinnen" und bleiben.

          nein, der letzte würde "gewinnen" und bleiben.

          Inwiefern verstehe ich Dich falsch? Willst Du bei Duplikaten bzw. Mehrfachvorkommen überhaupt keinen Eintrag davon mehr haben? Das ginge dann so:

          $unique = array();
          
          foreach ($zeile as $felder) {
            // Feld2 entspricht $felder[1] und Feld3 $felder[2]
            $key = sprintf('%s_%s', $felder[1], $felder[2]);
          
            if (array_key_exists($key, $unique)) {
          
              // zum Löschen markieren
              $unique[$key]['delete'] = 1;
          
            } else {
          
              // neuen Eintrag anlegen
              $unique[$key] = array(
                'Feld2' => $felder[1],
                'Feld3' => $felder[2]
              );
            }
          }
          
          // clean up
          foreach (array_keys($unique) as $key) {
          
            if (array_key_exists('delete', $unique[$key])) {
              unset ($unique[$key]);
            }
          }
          

          Liebe Grüße

          Felix Riesterer

          1. Hallo Felix,

            vielen lieben Dank für Deine Lösung und Hilfe!

            Ich habe mir Deinen Lösungsweg auf jeden Fall gespeichert, nehme aber die SQL-Lösung von Rolf, weil sie zum einen die einzelnen Datensätze nicht iterieren muß und zum anderen haargenau in meinen eh schon vorhandenen Importweg passt (temp-table und von dort aus in real_table).

            Trotzdem vielen Dank für Deinen Weg über php.

            Gruß, Pit

    4. Beide Probleme kannst Du über assoziative Arrays lösen. Mache aus jeder Zeile ein assiatives Array, also so daß die Spalten Namen bekommen. Dann kannst Du über array_merge() diese Felder namentlich überschreiben.

      Und die ganze Datei ist auch ein ass. Array. Entweder mit der Zeilennummer als Schlüssel oder mit einem Datenfeld was die Eindeutigkeit regelt.

      MFG

  5. Lieber Pit,

    ich habe in diesem Thread genügend gelesen, um Dein Ausgangsposting infrage zu stellen.

    Die Datei ist wie folgt aufgebaut:

    Feld1|Feld2|Feld3|Feld4|Feld5
    Feld1|Feld2|Feld3|Feld4|Feld5
    Feld1|Feld2|Feld3|Feld4|Feld5
    Feld1|Feld2|Feld3|Feld4|Feld5
    Feld1|Feld2|Feld3|Feld4|Feld5
    ...
    

    Nein, das ist sie offensichtlich nicht. Das, was Du uns als Vorgabe nennst, trifft in Wirklichkeit auf Dein Problem nicht zu. Damit verurteilst Du diesen Thread zu einer rein akademischen Übung, bei der unsere Antworten auf Dein Problem nur ungenügend passen können, da Du uns die Wahrheit verschweigst. Die sich unter diesen Umständen entwickelnde Diskussionen sind für die beteiligten Hilfswilligen sehr unbefriedigend. Warum nur mutest Du ihnen das zu?

    Wer kann mir mal unter die Arme greifen?

    Aber das willst Du doch offensichtlich nicht! Der ganze Thread zeigt, dass Du in Wirklichkeit etwas von Deinem Ursprungsposting abweichendes erreichen willst, für das Du uns aber gezielt wesentliche Informationen vorenthältst. Wenn wir Dir wirklich unter die Arme greifen sollen, müsstest Du uns reinen Wein einschenken. Damit gehst Du aber das Risiko ein, dass man Dir aufzeigt, dass Dein bisheriges Vorgehen im Kern dafür ungeeignet ist und eine Neukonzeption nötig hat. Dieses Risiko willst Du aber anscheinend vermeiden, da Du die dafür notwendigen Informationen ja eben zurückhältst.

    Solltest Du wirklich Hilfe wollen, dann sei ehrlich und nenne alle(!) wesentlichen Fakten. Ansonsten beutest Du unsere Hilfsbereitschaft in einer Weise aus, die unredlich ist.

    Liebe Grüße

    Felix Riesterer

    1. Hallo Felix,

      ich habe in diesem Thread genügend gelesen, um Dein Ausgangsposting infrage zu stellen.

      Die Datei ist wie folgt aufgebaut:

      Feld1|Feld2|Feld3|Feld4|Feld5
      Feld1|Feld2|Feld3|Feld4|Feld5
      Feld1|Feld2|Feld3|Feld4|Feld5
      Feld1|Feld2|Feld3|Feld4|Feld5
      Feld1|Feld2|Feld3|Feld4|Feld5
      ...
      

      Nein, das ist sie offensichtlich nicht.

      Doch, genau davon bin ich ausgegangen als ich das Post schrieb. Nach einigen Dateien aber kam mir eine Datei unter, die inmitten eines feldes eines csv-Satzes einen zeilenumbruch hatte. Erst dann kam ich auf die Idee, je Zeile die Anzahl der Delimiter zu zählen. Es ging also danach um einen Ausbau der vorhandenen Lösung, die prinzipiell gut funktionierte.

      Das, was Du uns als Vorgabe nennst, trifft in Wirklichkeit auf Dein Problem nicht zu. Damit verurteilst Du diesen Thread zu einer rein akademischen Übung, bei der unsere Antworten auf Dein Problem nur ungenügend passen können, da Du uns die Wahrheit verschweigst.

      Was bemängelst Du? Das eine csv-Datei nur 5 Spalten hat, eine andere aber 10? Und das ich im DB-Import alles nach Spalte 5 der csv in die db-Spalte namens "Bemerkungen" eintrage?

      Aber das willst Du doch offensichtlich nicht! Der ganze Thread zeigt, dass Du in Wirklichkeit etwas von Deinem Ursprungsposting abweichendes erreichen willst, für das Du uns aber gezielt wesentliche Informationen vorenthältst.

      Nicht ganz richtig. Das Thema hat sich entwickelt.

      Wenn wir Dir wirklich unter die Arme greifen sollen, müsstest Du uns reinen Wein einschenken.

      Reinen Wein? Übertreibst Du nicht ein bißchen? 😉

      Damit gehst Du aber das Risiko ein, dass man Dir aufzeigt, dass Dein bisheriges Vorgehen im Kern dafür ungeeignet ist und eine Neukonzeption nötig hat. Dieses Risiko willst Du aber anscheinend vermeiden, da Du die dafür notwendigen Informationen ja eben zurückhältst.

      Nochmal: Auf die Anzahl der Spalten der csv habe ich gar keinen Einfluß. Die db hat (z.b.) 5 notwendige Spalten. Die csv hat 5 (notwendige) + n Spalten. Von den n Spalten kann ich n-x Spalten nutzen, die ich für nötig erachte, also jene, die meiner Tabelle einen Mehrwert bringen. Diese Spalten werden dann aber nach und nach in die Spalte "Bemerkungen" übernommen. x Spalten verfallen. An diesem Modell kann ich nichts Falsches erkennen. Einzig könnte man darüber reden, ob ich die Bemerkungen normalisieren könnte. Muß man aber nicht.

      Solltest Du wirklich Hilfe wollen, dann sei ehrlich und nenne alle(!) wesentlichen Fakten. Ansonsten beutest Du unsere Hilfsbereitschaft in einer Weise aus, die unredlich ist.

      Schade, dass sich (für Dich) Dinge anscheinend nicht entwickeln können/dürfen/sollen. Oder aber, dass man ggf. etwas vergisst, zu einem bestimmten Zeitpunkt nicht oder noch nicht weiß oder auch Fehler bei der Fragestellung macht.

      Wie dem auch sei, ich hatte nie vor, hier irgendwen auszubeuten oder sonstwie unredlich zu sein. Und ich bin auch davon überzeugt, dass ich das nie gemacht habe, nicht mache und auch ganz sicher nicht machen werde! Ich hoffe, ich habe es Dir schlüssig erklärt. Wenn nicht, kann ichs aber auch nicht ändern.

      Traurig genug, dass Du das anscheinend anders siehst/gesehen hast!

      Pit

      1. n'Abend Pit,

        ich habe zwar bisher nicht Sachliches beigetragen, aber jetzt mische ich mich doch noch als stellenweise genervter Mitleser ein. Genervt, weil ich auch den Eindruck hatte, dass du mit Informationen nur häppchenweise um die Ecke kommst, anstatt gleich die Karten auf den Tisch zu legen. Als jemand, der gewillt ist, das Thema anzupacken und zu helfen, fühlt man sich dann ausgenutzt oder veräppelt.

        Die Datei ist wie folgt aufgebaut:

        Feld1|Feld2|Feld3|Feld4|Feld5
        Feld1|Feld2|Feld3|Feld4|Feld5
        Feld1|Feld2|Feld3|Feld4|Feld5
        Feld1|Feld2|Feld3|Feld4|Feld5
        Feld1|Feld2|Feld3|Feld4|Feld5
        ...
        

        Nein, das ist sie offensichtlich nicht.

        Doch, genau davon bin ich ausgegangen als ich das Post schrieb.
        Nach einigen Dateien aber kam mir eine Datei unter, die inmitten eines feldes eines csv-Satzes einen zeilenumbruch hatte.

        Daher also die Idee, die Zahl der Delimiter (also eigentlich die Zahl der Felder pro Datensatz) zu zählen und zu überprüfen. Ein kleiner Nebensatz an der passenden Stelle, der uns sagt, dass das eine neue Erkenntnis ist, wäre völlig ausreichend gewesen. So aber wirkt es, als hättest du das von Anfang an gewusst, aber "vergessen" zu erwähnen.

        Abgesehen davon: Eine CSV-Datei, die an unpassender Stelle einen Zeilenumbruch hat, würde ich als ungültig abweisen.

        Erst dann kam ich auf die Idee, je Zeile die Anzahl der Delimiter zu zählen. Es ging also danach um einen Ausbau der vorhandenen Lösung, die prinzipiell gut funktionierte.

        Angenommen, du holst Zeile für Zeile mit fgetcsv(), dann erledigt diese Funktion das Zählen für dich. Und wenn in der Datei auch nur eine Zeile vorkommt, die zu viele oder zu wenige Felder hat - Fehler beim Import, Eingabedatei ungültig!

        Das, was Du uns als Vorgabe nennst, trifft in Wirklichkeit auf Dein Problem nicht zu.

        Will heißen: Du sagst, du müsstest CSV-Dateien in eine Datenbank importieren. Die CSV-Dateien seien nicht immer ganz "sauber", deshalb muss deine Import-Funktion eine gewisse Fehlertoleranz haben.
        Gut, akzeptiert. Aber dann kommst du mit der Neuigkeit raus, du selbst würdest die CSV-Dateien aus Excel generieren. Ja mei, dann hast du das Ergebnis doch auch selbst in der Hand und brauchst nicht am Ende nachzubessern, sondern kannst es gleich am Anfang richtig machen.

        Was bemängelst Du?
        Das eine csv-Datei nur 5 Spalten hat, eine andere aber 10?

        Nein. Verschiedene Tabellen haben eine unterschidliche Anzahl von Spalten. Aber eine Import-Tabelle (Excel, CSV), die für einen bestimmten Zweck gedacht ist, muss eine bestimmte, vorgegebene Struktur haben. Dazu gehört nicht nur die Anzahl der Felder, sondern auch deren Typ (z.B. Zahl, Datum, Text). Diese Struktur ist von der Anwendung vorgegeben, für die die Daten gedacht sind. Passt da beim Import etwas nicht - Fehler beim Import, Eingabedatei ungültig!

        Und das ich im DB-Import alles nach Spalte 5 der csv in die db-Spalte namens "Bemerkungen" eintrage?

        Das ist ein ungewöhnliches Vorgehen. Das bedeutet, deine CSV-Datei kann 6 oder mehr Felder haben, und die Werte der Felder 6..n landen alle zusammen in einer Spalte in der DB?

        Wenn wir Dir wirklich unter die Arme greifen sollen, müsstest Du uns reinen Wein einschenken.

        Reinen Wein? Übertreibst Du nicht ein bißchen? 😉

        Ja, Felix übertreibt vielleicht ein bisschen. Aber nicht viel.

        Nochmal: Auf die Anzahl der Spalten der csv habe ich gar keinen Einfluß.

        Aber sicher doch! Wenn du sie aus einer Excel-Tabelle erzeugst, bist du der Einzige, der einen Einfluss darauf hat.

        Solltest Du wirklich Hilfe wollen, dann sei ehrlich und nenne alle(!) wesentlichen Fakten. Ansonsten beutest Du unsere Hilfsbereitschaft in einer Weise aus, die unredlich ist.

        Schade, dass sich (für Dich) Dinge anscheinend nicht entwickeln können/dürfen/sollen. Oder aber, dass man ggf. etwas vergisst, zu einem bestimmten Zeitpunkt nicht oder noch nicht weiß oder auch Fehler bei der Fragestellung macht.

        Das ist völlig okay, solange nicht der Eindruck entsteht, das sei schon immer so gewesen und du hättest es bloß nicht erwähnt.

        Übrigens hätte ich noch einen ganz anderen möglichen Ansatz: Wenn die Daten aus einer Excel-Tabelle kommen, bietet es sich doch an, sie direkt in Excel passend aufzubereiten und von da direkt in die DB zu schreiben. AFAIK hat Excel einen SQL-Client integriert (im Detail wahrscheinlich MSSQL, es wäre zu prüfen, ob MySQL auch geht), und VBA ist auch kein Hexenwerk.

        Ciao,
         Martin

        --
        Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
        1. @Der Martin

          einem Kunden erklären daß seine Daten/Dateien ungültig sind, dürfte erfahrungsgemäß schwierig werden: Weil es der Erwartungshaltung eines Kunden widerspricht.

          MFG

          1. Hallo,

            einem Kunden erklären daß seine Daten/Dateien ungültig sind, dürfte erfahrungsgemäß schwierig werden:

            Falls es hier tatsächlich zu einem Kundengespräch käme, wäre Pit sein eigener Kunde…

            Gruß
            Kalk

            1. Hi,

              einem Kunden erklären daß seine Daten/Dateien ungültig sind, dürfte erfahrungsgemäß schwierig werden:

              Falls es hier tatsächlich zu einem Kundengespräch käme, wäre Pit sein eigener Kunde…

              so habe ich das auch verstanden.

              Aber was soll's, ich habe mir selbst auch schon hin und wieder gesagt: "Das ist aber Sch****, was du da gemacht hast."

              Ciao,
               Martin

              --
              Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
            2. Hallo,

              einem Kunden erklären daß seine Daten/Dateien ungültig sind, dürfte erfahrungsgemäß schwierig werden:

              Falls es hier tatsächlich zu einem Kundengespräch käme, wäre Pit sein eigener Kunde…

              Ja wenn das so ist, da frag ich mich natürlich warum es denn ausgerechnet eine CSV Datei sein muss. Zumal es für Datenexport /Datenimport auch in PHP einen Serializer gibt. Entscheidender als das Dateiformat ist die Frage wie man mit Feldern verfährt für die es keine Daten gibt: Sowas regelt man über's DB-Design. Ebenso ist die Frage wie man mit Duplikaten umgeht auch eine Frage des DB-Design.

              MFG

        2. Hallo Martin,

          Abgesehen davon: Eine CSV-Datei, die an unpassender Stelle einen Zeilenumbruch hat, würde ich als ungültig abweisen.

          Mein Programm bekommt solche Dateien vom Kunden. Es muss doch zulässig sein, auch Daten aus der Feldart textarea mit Zeilenumbrüchen per CSV zu transportieren. Ja klar, da kann die CSV-Zeile zu früh enden und die nächste(n) Zeile(n) sind der Rest-Schrott.

          Da könnte man pro Zeile die CSV-Feldtrenner z.B. das ; zählen. Wenn sie nicht reichen, den Zeilenumbruch von \n auf <br> ändern und die nächste(n) Zeile(n) anhängen.

          Gruß, Linuchs

          1. Hi,

            Mein Programm bekommt solche Dateien vom Kunden. Es muss doch zulässig sein, auch Daten aus der Feldart textarea mit Zeilenumbrüchen per CSV zu transportieren.

            Natürlich. Es ist nur sehr naiv, anzunehmen, daß ein Datensatz (das, was in Excel einer Zeile entspricht) in einer CSV-Datei nur aus einer Zeile besteht.

            Da könnte man pro Zeile die CSV-Feldtrenner z.B. das ; zählen.

            Wobei das Trennzeichen auch wieder im Feldinhalt vorkommen kann.

            Wenn sie nicht reichen, den Zeilenumbruch von \n auf <br> ändern und die nächste(n) Zeile(n) anhängen.

            Einfach einen CSV-Parser verwenden, der kümmert sich darum, daß Zeilenumbrüche oder Feldtrennzeichen oder Feldbegrenzer-Zeichen in Feldinhalten richtig berücksichtigt werden.

            cu,
            Andreas a/k/a MudGuard

          2. Hallo,

            Abgesehen davon: Eine CSV-Datei, die an unpassender Stelle einen Zeilenumbruch hat, würde ich als ungültig abweisen.

            Mein Programm bekommt solche Dateien vom Kunden.

            und wie geht das Programm damit um? Sind die Zeilenumbrüche irgendwie maskiert?

            Es muss doch zulässig sein, auch Daten aus der Feldart textarea mit Zeilenumbrüchen per CSV zu transportieren. Ja klar, da kann die CSV-Zeile zu früh enden und die nächste(n) Zeile(n) sind der Rest-Schrott.

            Ich bin mir gerade nicht sicher ... ich meine aber, dass ein Umbruch in CSV möglich ist, wenn die Zeile mit einem Backslash endet, der den Zeilenumbruch maskiert. Ist aber unüblich, und nur wenige Programme machen da mit.

            [...] den Zeilenumbruch von \n auf <br> ändern und die nächste(n) Zeile(n) anhängen.

            Wenn die erzeugende und die verarbeitende Software sich einig sind, kann man selbstverständlich auch eine übliche Escape-Sequenz wie z.B. \n verwenden. Dann muss man aber sicherstellen, dass wirklich alle beteiligten Programme damit zurechtkommen.

            Ciao,
             Martin

            --
            Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
            1. Hallo Martin,

              mag sein, dass das mit Maskierungen funktioniert - aber mein Kenntnisstand ist, dass Felder mit ; oder \n darin in " gesetzt werden. Sollte im Feldinhalt ein " enthalten sein, wird der Inhalt in " engeschlossen und das " darin zu "" verdoppelt.

              Ersetze ; \n und " nach Bedarf durch die konkrete Ausprägung in der CSV Datei.

              Rolf

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

                mag sein, dass das mit Maskierungen funktioniert - aber mein Kenntnisstand ist, dass Felder mit ; oder \n darin in " gesetzt werden.

                das eine hat IMO mit dem anderen nichts zu tun; außerdem empfehle ich, Felder mit Strings als Wert immer in Anführungszeichen zu setzen.

                Sollte im Feldinhalt ein " enthalten sein, wird der Inhalt in " engeschlossen und das " darin zu "" verdoppelt.

                Ja.

                Ersetze ; \n und " nach Bedarf durch die konkrete Ausprägung in der CSV Datei.

                Womit wir an einem wichtigen Punkt angekommen sind: Das CSV-Format ist sehr vage spezifiziert und lässt viele Freiheiten zu. Das macht es als universelles Austauschformat problematisch, weil jeder CSV-Parser (oder Generator) wieder geringfügig andere "Macken" hat.

                Frohes Schaffen,
                 Martin

                --
                Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
          3. Abgesehen davon: Eine CSV-Datei, die an unpassender Stelle einen Zeilenumbruch hat, würde ich als ungültig abweisen.

            Mein Programm bekommt solche Dateien vom Kunden. Es muss doch zulässig sein, auch Daten aus der Feldart textarea mit Zeilenumbrüchen per CSV zu transportieren.

            Der Zeilenumbruch einer <textarea> ist mit 0x0D 0x0A kodiert. Das ist also nicht nur ein \n sondern \r\n (2 Bytes, CRLF).

            MFG

            1. Lieber pl,

              Der Zeilenumbruch einer <textarea> ist mit 0x0D 0x0A kodiert. Das ist also nicht nur ein \n sondern \r\n (2 Bytes, CRLF).

              das ist bei Windows so. Auf anderen Systemen nicht unbedingt. Das solltest Du unbedingt dazu sagen, sonst erzählst Du nur die halbe Wahrheit.

              Liebe Grüße

              Felix Riesterer

              1. Der Zeilenumbruch einer <textarea> ist mit 0x0D 0x0A kodiert. Das ist also nicht nur ein \n sondern \r\n (2 Bytes, CRLF).

                das ist bei Windows so.

                Es ist generell so! MFG

              2. Der Zeilenumbruch einer <textarea> ist mit 0x0D 0x0A kodiert. Das ist also nicht nur ein \n sondern \r\n (2 Bytes, CRLF).

                das ist bei Windows so. Auf anderen Systemen nicht unbedingt.

                Der Artikel bezieht sich auf Textdateien. Also bitte richtig lesen, ich schrieb <textarea> und da ist 0D 0A (CRLF) der Zeilenumbruch.

                MFG

              3. Hallo Felix,

                Der Zeilenumbruch einer <textarea> ist mit 0x0D 0x0A kodiert. Das ist also nicht nur ein \n sondern \r\n (2 Bytes, CRLF).

                das ist bei Windows so. Auf anderen Systemen nicht unbedingt. Das solltest Du unbedingt dazu sagen, sonst erzählst Du nur die halbe Wahrheit.

                Nene. Da hat Rolf schon recht. Newlines in Textareas sind definiert als CR LF. Siehe auch den Standard:

                The element's value is defined to be the element's raw value with the following transformation applied:

                • Replace every occurrence of a U+000D CARRIAGE RETURN (CR) character not followed by a U+000A LINE FEED (LF) character, and every occurrence of a U+000A LINE FEED (LF) character not proceeded by a U+000D CARRIAGE RETURN (CR) character, by a two-character string consisting of a U+000D CARRIAGE RETURN - U+000A LINE FEED (CRLF) character pair.
                • If the element's wrap attribute is in the Hard state, insert U+000D CARRIAGE RETURN - U+000A LINE FEED (CRLF) character pairs into the string using a UA-defined algorithm so that each line so that each line has no more than character width characters. The purposes of this requirement, lines are delimited by the start of the string, the end of the string, and U+000D CARRIAGE RETURN - U+000A LINE FEED (CRLF) character pairs.

                Schwierig wird es erst, wenn man das mit JS bearbeitet. Da gibt es dann wieder Plattform-Unterschiede… /\n/ matcht in JS nicht immer auf den gleichen String, abhängig von Browser und OS, weshalb man dann doch wieder auf /\r\n|\r|\n/ zurückgreifen muss.

                Freundliche Grüße,
                Christian Kruse

                1. Hallo Christian,

                  das finde ich jetzt merkwürdig. Wenn die Textarea \r\n als Line Delimiter enthält, sind das doch definitiv 2 Zeichen. Und die habe ich bei einem Codepoint-by-Codepoint Dump des Content nicht gesehen - siehe hier [https://forum.selfhtml.org/self/2020/jan/16/csv-datei-von-duplikaten-befreien/1763257#m1763257].

                  Rolf

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

                    das finde ich jetzt merkwürdig. Wenn die Textarea \r\n als Line Delimiter enthält, sind das doch definitiv 2 Zeichen. Und die habe ich bei einem Codepoint-by-Codepoint Dump des Content nicht gesehen - siehe hier [https://forum.selfhtml.org/self/2020/jan/16/csv-datei-von-duplikaten-befreien/1763257#m1763257].

                    Ja, das ist Teil der Normalisierung für JS. Andere API 😉 Wenn du das Formular allerdings absendest, wirst du sehen, dass dort aus Zeilenumbrüchen 0x0D 0x0A wird. Einfachstes Beispiel:

                    <!DOCTYPE html>
                    <html>
                      <head>
                        <meta charset="utf-8" />
                        <title>Test</title>
                      </head>
                      <body>
                        <form>
                          <textarea name="foo"></textarea>
                          <button type="submit">absenden</button>
                        </form>
                      </body>
                    </html>
                    

                    Hier wirst du, wenn du in der Textarea einen Zeilenumbruch einfügst, sehen, dass im Query-String foo=%0D%0A%0D%0A auftaucht.

                    Und ja, das ist alles konfus und verwirrend.

                    Freundliche Grüße,
                    Christian Kruse

                    1. Hallo Christian,

                      Hier wirst du, wenn du in der Textarea einen Zeilenumbruch einfügst, sehen, dass im Query-String foo=%0D%0A%0D%0A auftaucht.

                      das wären schon zwei Zeilenumbrüche. Wo kommt der zweite her?

                      Und ja, das ist alles konfus und verwirrend.

                      Find' ich auch.

                      Ciao,
                       Martin

                      --
                      Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
                      1. Hallo Martin,

                        Hier wirst du, wenn du in der Textarea zwei Zeilenumbrüche einfügst, sehen, dass im Query-String foo=%0D%0A%0D%0A auftaucht.

                        das wären schon zwei Zeilenumbrüche. Wo kommt der zweite her?

                        Wieso, ich schrieb doch von zwei Zeilenumbrüchen? 😜

                        Freundliche Grüße,
                        Christian Kruse

                        1. Hallo,

                          Hier wirst du, wenn du in der Textarea zwei Zeilenumbrüche einfügst, sehen, dass im Query-String foo=%0D%0A%0D%0A auftaucht.

                          das wären schon zwei Zeilenumbrüche. Wo kommt der zweite her?

                          Wieso, ich schrieb doch von zwei Zeilenumbrüchen? 😜

                          hey, du schummelst! ;-)
                          Ich spiel nicht mehr mit, gebt mir meinen Teddy wieder!

                          Ciao,
                           Martin

                          --
                          Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
                    2. Hallo Christian,

                      ok, offenbar sind Chrome und Firefox sich da einig, aber wenn man so durch StackOverflow streift, ist es in anderen Browsern wohl anders (gewesen). Angeblich auch im IE - aber nicht im IE11.

                      Die weitere Frage ist dann, was ein WAMP und LAMP System daraus macht. Oder ein WIXP[^1] oder gar ein WISA[^2]

                      [^1] Windows, IIS, (irgendeine DB), PHP [^2] Windows, IIS, MS SQL Server - da will ich kein X sehen, ASP.NET

                      Bei LAMP würde ich eigentlich annehmen wollen, dass es aus \r\n einen standardisierten Unix-Linebreak \n macht. Kann ich aber nicht testen. Oder bin ich da gezwungen, die \r manuell zu entfernen falls ich den Text nach Unix-Standards weiterverarbeiten will?

                      Bei WAMP dagegen, dass ich einen standardisierten Windows Linebreak \r\n bekomme, egal ob %0D%0A oder nur %0A über den Draht kommt. Das ist aber nicht so.

                      D.h. mein PHP Programm kann davon ausgehen, dass es aus einer Textarea \r\n hereinbekommt, und muss das plattformkorrekt in Line Delimiters umsetzen. In der DB speichert man am besten noch \r\n, damit der ausgegebene Text zum eingegebenen passt, und formatiert es nur für externe Dateien um.

                      Habichdasrichtigverstanden?

                      Rolf

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

                        ok, offenbar sind Chrome und Firefox sich da einig, aber wenn man so durch StackOverflow streift, ist es in anderen Browsern wohl anders (gewesen). Angeblich auch im IE - aber nicht im IE11.

                        Ja, in vergangenen Zeiten war das Kraut und Rüben. Das ist der Ist-Zustand bereits eine Verbesserung.

                        Bei LAMP würde ich eigentlich annehmen wollen, dass es aus \r\n einen standardisierten Unix-Linebreak \n macht. Kann ich aber nicht testen.

                        Ich kann nicht für alle PHP-Versionen sprechen, aber bei allen mir bekannten Versionen bleibt bei einem LAMP-System das CR LF erhalten.

                        Oder bin ich da gezwungen, die \r manuell zu entfernen falls ich den Text nach Unix-Standards weiterverarbeiten will?

                        Ja.

                        D.h. mein PHP Programm kann davon ausgehen, dass es aus einer Textarea \r\n hereinbekommt, […]

                        Nein. 😉 Du kannst nicht davon ausgehen, denn nicht alle Requests werden von modernen Browsern ausgeführt. Du musst normalisieren. Ich mache das hier im Forum auch:

                        Enum.reduce(str_changes, changeset, fn key, changeset ->
                          Ecto.Changeset.update_change(changeset, key, &Regex.replace(~r/\015\012|\015|\012/, &1, "\n"))
                        end)
                        

                        und muss das plattformkorrekt in Line Delimiters umsetzen.

                        Ja.

                        In der DB speichert man am besten noch \r\n, damit der ausgegebene Text zum eingegebenen passt, und formatiert es nur für externe Dateien um.

                        Habichdasrichtigverstanden?

                        Ich handhabe das anders. Ich speichere in der Datenbank immer nur Unix line endings. Wenn notwendig, konvertiere ich dann in das benötigte Format.

                        Freundliche Grüße,
                        Christian Kruse

            2. Hallo pl,

              innerhalb des Browsers ist ein Zeilenumbruch, dem man dort eingibt, stets ein \n (0x0a). Selbst dann, wenn man die Textarea mit Fremd-Linedelimitern bestückt:

              document.getElementById("foo").value = "Das ist ein Test\r\nEr ist die Pest\rEr gibt mir den Rest\n";
              

              führt zum Hexdump (Chrome und Firefox unter Windows)

              44 61 73 20 69 73 74 20 65 69 6e 20 54 65 73 74 a 45 72 20 69 73 74 20 64 69 65 20 50 65 73 74 a 45 72 20 67 69 62 74 20 6d 69 72 20 64 65 6e 20 52 65 73 74 a
              

              also jedesmal \n. Du kannst das hiermit gerne unter Linux ausprobieren.

              Was PHP daraus macht, nachdem man einen solchen String zum Server gesendet hat, ist eine andere Frage.

              Update: Ich habe Christians Hinweis auf den Standard jetzt erst gesehen und bin nun verwirrt.

              Rolf

              --
              sumpsi - posui - clusi
              1. Die Zeilenende-Controls macht der Browser, das hat mit PHP nichts zu tun. Das kannst Du selber prüfen indem Du im Formular einen Zeilenumbruch machst

                <form>
                <textarea name="t"></textarea>
                <button>...</button>
                </form>
                

                und dann guckst was nach dem Klick auf den Button in der Adresszeile steht: ?t=%0D%0A

                Davon abgesehen sollte der Transport von solchen Dingen wie Zeilenumbruchkodierung unabhängig sein. Da ist es schon ein bischen ungeschickt eine CSV Datei zu nehmen.

                MFG

      2. Lieber Pit,

        ich habe nichts gegen Entwicklung. Wenn sie transparent ist, kann man sie als solche erkennen und akzeptieren. Die Transparenz hast Du nun ebenso wie zuvor schon andere Details nachgereicht.

        OK.

        Nach einigen Dateien aber kam mir eine Datei unter, die inmitten eines feldes eines csv-Satzes einen zeilenumbruch hatte.

        Siehst Du, jetzt reden wir wieder über ein neues aber wichtiges Detail. Ein Zeilenumbruch. Soll das bedeuten, dass in einer Zelle Inhalt mit Zeilenumbruch gestanden hat? Und der bringt Dir nun Deine CSV-Datei durcheinander. Das kann ich mir vorstellen, wenn die Datei nicht korrekt erstellt und dann geparst wurde. Aber die Notwendigkeit des Umwegs über eine CSV-Datei leuchtet mir noch immer nicht ein. @Der Martin schlägt einen direkten Export zum SQL-Server vor, ich schlage eine direkte php-seitige Auswertung der Excel-Datei selbst anstelle ihres CSV-Exportes vor. Dort blieben die Zeilenumbrüche auch in ihrer "Zeile" und würden keine unerwartete CSV-Zeile erzeugen.

        Erst dann kam ich auf die Idee, je Zeile die Anzahl der Delimiter zu zählen. Es ging also danach um einen Ausbau der vorhandenen Lösung, die prinzipiell gut funktionierte.

        Nein, aus meiner Sicht geht es um ein konzeptionelles Problem, das der Umweg über eine CSV-Datei mit sich bringt. Würdest Du die Excel-Datei direkt parsen und auswerten, würde sich dieses Problem nicht stellen.

        Was bemängelst Du? Das eine csv-Datei nur 5 Spalten hat, eine andere aber 10? Und das ich im DB-Import alles nach Spalte 5 der csv in die db-Spalte namens "Bemerkungen" eintrage?

        Nein, dass Du uns die Beobachtungen nur häppchenweise lieferst. Man kann das auch gründlich zusammenfassen und in ein Posting stecken. Dann ist die Ursachenforschung auch wesentlich effektiver und eine Lösungsstrategie zielsicherer zu entwickeln. Wie z.B. dass Du den Umweg über CSV besser ganz lässt.

        Reinen Wein? Übertreibst Du nicht ein bißchen? 😉

        Vielleicht. Aber nicht unberechtigterweise.

        Nochmal: Auf die Anzahl der Spalten der csv habe ich gar keinen Einfluß.

        Du hältst Dich an diesem CSV-Ergebnis fest wie ein Ertrinkender an einem Strohhalm. Deshalb drehen wir uns hier im Kreis! Natürlich hast Du jeden Einfluss, wenn Du die Excel-Datei selbst in eine CSV-Datei umwandelst! Vielleicht nur solltest Du die Feldinhalte in doppelte Anführungszeichen setzen lassen und beim CSV-Parsen darauf Rücksicht nehmen, damit nicht durch einen Zeilenumbruch in diesen Inhalten das CSV-Muster der x Zellen pro Zeile zerstört wird!

        Die db hat (z.b.) 5 notwendige Spalten.

        Das alles interessiert mich herzlich wenig. Mich interessiert lediglich der Datentransport von Excel nach PHP, weil die Daten von dort aus in die DB geschrieben werden.

        An diesem Modell kann ich nichts Falsches erkennen. Einzig könnte man darüber reden, ob ich die Bemerkungen normalisieren könnte. Muß man aber nicht.

        Das klingt doch einigermassen danach, als ob Du eben keine Hilfe wolltest, weil sie Deine gefasste Meinung über die ideale Lösung infrage stellt. Lag ich mit meinem Vorwurf also wirklich so falsch?

        Wie dem auch sei, ich hatte nie vor, hier irgendwen auszubeuten oder sonstwie unredlich zu sein. Und ich bin auch davon überzeugt, dass ich das nie gemacht habe, nicht mache und auch ganz sicher nicht machen werde!

        Dann ist ja gut. Ich kann den Thread ja weiter beobachten.

        Traurig genug, dass Du das anscheinend anders siehst/gesehen hast!

        So ganz alleine stehe ich mit dieser Beobachtung ja nicht da. Aber schauen wir doch, ob sich nicht doch ein besserer Lösungsansatz für Dein tatsächliches Problem "Umweg über CSV ist manchmal unzuverlässig" finden lässt. Nur musst Du es auch als solches akzeptieren, denn ohne eine solche Einsicht drehen wir uns hier weiter im Kreis.

        Liebe Grüße

        Felix Riesterer

      3. Hallo Pit,

        die inmitten eines feldes eines csv-Satzes einen zeilenumbruch hatte

        Falls dieses Feld in Anführungszeichen stand, dann sollte das etwas sein, das von fgetcsv behandelt wird. Jedenfalls ist das kein Grund, mittels Statistiken die häufigste Satzlänge zu ermitteln.

        In dem Fall wärest Du in die A-B (oder XY) Problem Falle getappt.

        Ist das Problem "unterschiedliche Anzahl Felder" identisch mit dem Problem "Zeilenumbruch"? Mein Excel schließt Felder mit Zeilenumbruch, Feldtrennzeichen oder Anführungszeichen darin automatisch in Anführungszeichen ein, und Anführungszeichen im Feld werden verdoppelt gespeichert. Wird von fgetcsv alles transparent gehandhabt. Einsatz von fgetcsv wäre also die korrekte Problemlösung.

        Viele Datenbank-Import-Tools beherrschen Zeilenumbrüche in CSV Dateien nicht.

        Rolf

        --
        sumpsi - posui - clusi
      4. Aha, so wie es sich jetzt darstellt, kommen die Daten aus einer DB/Tabelle. Da würde ich aber nicht erst in der CSV Datei versuchen die Duplikate zu bereinigen sondern genau das spätestens beim Datenexport tun. Das macht die Sache erheblich einfacher.

        MFG

        1. Aha, so wie es sich jetzt darstellt, kommen die Daten aus einer DB/Tabelle. Da würde ich aber nicht erst in der CSV Datei versuchen die Duplikate zu bereinigen sondern genau das spätestens beim Datenexport tun. Das macht die Sache erheblich einfacher.

          Falls andersherum, CSV => DB:

          Die CSV-Datei in den Hauptspeicher lesen, dabei bekommen die Spalten Namen, eine dem enstprechende Stuktur sähe in etwa so aus:

          1: {name: foo, vname: bar},
          2: {name: dig, vname: dog}
          

          usw. wobei 1, 2 einen Tupel eindeutig kennzeichnen. Um Redundanzen bestimmter Datenfelder rauszufiltern würde ich für die Tabelle dem entsprechende (eindeutige) Schlüsselfelder festlegen.

          MFG

      5. Ich kapier nicht ganz, was Du machst. Das ist für mich alles "von hinten durch die Brust ins Auge".

        • Schritt 1:

        Exportiere aus Excel ganz normales CSV - just mit den von Excel vorgeschlagenen Optionen.

        • Schritt 2:

        Importiere das Zeug in die Datenbank, meinetwegen PHP. Beide können mit dem von Excel - bei Wahl der Standard-Optionen - geliefertem Zeug sehr gut umgehen.

        • Beachte:

        Alles, was Dir Probleme bereitet sind die von Dir willkürliche gewählten Umwege. Da gibt es nämlich Ecken.