Visualizzazione dei risultati da 1 a 8 su 8
  1. #1
    Utente di HTML.it
    Registrato dal
    Dec 2005
    Messaggi
    714

    Importare da un file CSV 200 mila righe

    Ciao a tutti,
    come da oggetto devo popolare il mio database, quindi fare un importazione, dei dati da un file CSV di più di 200 mila righe.

    Il sw che fa l'importazione è strutturato cosi: Apre il file, conta le righe, esegue un ciclo partendo da 1 (0 e' l'intestazione) e inizia a fare le query di insert. Il problema e' che dopo circa, 80 mila record inseriti il browser va in errore.

    Ad ogni iterazione del ciclo metto l'istruzione

    set_time_limit(0);

    e ogni 10000 righe, faccio uno sleep(5).

    Ho provato anche ad accumulare le insert, quindi invece di fare 80 mila query, gnene faccio fare 8 mila da 10 inserimenti l'una, ma anche cosi, niente da fare.

    Avete consigli su come fare, tenendo presente che l'importazione deve essere fatta in uno step, quindi senza splittare il documento.

    Grazie.
    Antonio

  2. #2
    Utilizza questo algoritmo:


    Avvia la sessione
    Apri il file e conta le righe
    - Verifica di non avere un importazione in corso
    - In caso affermativo esegui un seek nel file fino alla riga dove hai terminato l'importazione
    Esegui l'importazione del 5% delle righe
    Salva il numero di riga in sessione
    Invia in output un HTML con la visualizzazione del progresso ed un javascript o meta refresh per eseguire il redirect sulla stessa pagina

  3. #3
    Utente di HTML.it
    Registrato dal
    Dec 2005
    Messaggi
    714
    Non male come idea!! Ci provo...

  4. #4
    Utente di HTML.it
    Registrato dal
    Dec 2005
    Messaggi
    714
    Funziona perfettamente!!

    E' piu' veloce, e posso addirittura monitorare la situazione, sapendo con precisione a che percentuale sono. Per essere ancora piu' preciso e veloce potrei abbassare i valori e invece del 5% faccio 1% alla volta.

    Personalizzandolo si puo' fare come una barra verde di loading

    Grazie per la dritta, grazie davvero

  5. #5
    direi che c'è un sistema che è ancora più efficente se ti scrivi un piccolo parser per i file csv

    facendo cosi devi interrompere e ricominciare le operazioni, ma se:
    - apri il file
    - inizializzi una variabile che ti funge da buffer
    - avvii un loop dove leggi 8kb dal file e lo accodi (ergo fai un .=) al buffer
    - inizializzi una variabile che ti funge da stato del parser, ovvero se il parser è dentro o fuori dagli apici (fuori, dentro apici singoli o dentro apici doppi)
    - inizializzi una variabile che ti senga l'inizio del contenuto
    - utilizzando strpos e cerchi le virgole, gli a capo e i due tipi di apici se il parser è nello stato fuori altrimenti se è nello stato dentro uno dei due tipi di apici cerchi esclusivamente quello
    - dopo di che dipende cosa trovi e dipende dallo stato del parser:
    * se sei fuori e trovi un apice singolo o doppio, ti segni l'inizio della posizione incrementato di uno ed imposti lo stato relativo all'apice
    * se sei dentro uno dei due tipi di apice, e trovi il corrispettivo, utilizzi substr per estrarre il blocco tra i due apici (basta una sottrazione tra la posizione dell'apice iniziale e la posizione corrente)
    * se sei fuori e trovi una virgola, verifichi se avevi già estratto il testo e se si ti sposti alla colonna successiva reimpostando la posizione iniziale del testo e lo switch che ti indica se avevi già letto il testo dagli apici singoli/doppi, altrimenti estrai il testo letto, come per gli apici, ti segni la nuova posizione iniziale del testo che corrisponde alla posizione iniziale più uno e continui la letura
    * se sei fuori e trovi un \r\n, \n o \r prendi l'elenco dei testi estratti e te lo inserisci nell'elenco delle righe per poi resettare l'offset della posizione iniziale del testo su lui più un carattere
    * se strpos ti restituisce false (=== false) vuol dire che non hai trovato altro quindi ti basta prendere il valore dell'ultima posizione iniziale del testo e fare $buffer = substr($buffer, $offset_testo) in modo da preservare la parte non elaborata

    Quando il sistema termina sia secondo ciclo (quello interno) ed il primo vuol dire che tutto il file è stato letto correttamente. Puoi con semplicità introdurre check semplicemente conteggiando il numero di colonne presenti nella prima riga e controllando poi che le righe siano composte dallo stesso numero di righe e che il buffer alla fine sia vuoto.

    Questo sistema, oltre a consumare pochissima memoria (8kb e qualcosa per volta), ha anche il vantaggio di non parsare carattere per carattere tramite codice php ma lo fa fare a php stesso usando strpos
    The fastest Redis alternative ... cachegrand! https://github.com/danielealbano/cachegrand

  6. #6

  7. #7
    Originariamente inviato da filippo.toso
    Perchè fare tanta fatica se esiste http://www.php.net/fgetcsv ?
    era una soluzione come un altra
    The fastest Redis alternative ... cachegrand! https://github.com/danielealbano/cachegrand

  8. #8
    giusto per fare una prova, fgetcsv consuma qualcosa come 57mb di memoria (letti tramite memory_get_usage e non memory_get_usage_peak che significa che in quel momento c'era usata tutta quella memoria inutilmente)

    sto scribbacchiando il parser al volo per fare un test

    edit:

    consuma 57mb di memoria su un file da 200 mila righe con 10 colonne
    La riga tipo è
    codice:
    Riga{$NUMER_RIGA}-Colonna1,Colonna2,Colonna3,Colonna4,Colonna5,Colonna6,Colonna7,Colonna8,Colonna9,Colonna10
    EDIT:

    siccome sono fuso del tutto, quelli erano 57kb non 57mb mi stavo iniziando seriamente a preoccupare
    The fastest Redis alternative ... cachegrand! https://github.com/danielealbano/cachegrand

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.