Gerar CSV com >= 300 mil linhas

1,211 views
Skip to first unread message

Celso

unread,
Mar 24, 2009, 12:29:48 PM3/24/09
to php-brasil
Olá Pessoal!
Estou com um probleminha:
Preciso gerar um csv com umas 300 mil linhas ! Já tentamos várias
técnicas, mas infelizmente nada: A memória não aguenta...
Nós fazemos uma iteração de um array, pra cada iteração damos um fwrite
(string...)


Desde já, grato
Celso.

Roberto C. Silva

unread,
Mar 24, 2009, 2:33:19 PM3/24/09
to php-b...@googlegroups.com
Tenta gerar o arquivo pelo banco de dados.
se for o mysql ficaria mais ou menos assim
 
select concat("'", campo1, "'; '", campo2,"'") into outfile '/tmp/arquivo.csv' from tabela;
isto seria uma forma.
[]s

 
2009/3/24 Celso <cel...@gmail.com>

Er Galvao Abbott

unread,
Mar 24, 2009, 3:13:56 PM3/24/09
to php-b...@googlegroups.com
O Array fica armazenado na memória, o que provavelmente seja a causa do
seu problema.

Você pode tentar uma combinação da função array_walk (que aplica uma
função callback em um array inteiro) em conjunto com a função
array_shift (que remove o primeiro elemento do array), de forma que a
cada iteração da função callback o array seja reduzido em um elemento
(coun(array) - 1), assim sendo quanto mais você for escrevendo no
arquivo, menor o array vai ficando, liberando memória.

Abraços,

Galvão
http://www.galvao.eti.br/

Gustavo Campos

unread,
Mar 24, 2009, 3:14:39 PM3/24/09
to php-b...@googlegroups.com
Ou pode fazer sua consulta em intervalos menores, digamos de 10.000 em 10.000 linhas, e ir concatenando no final do arquivo =D
--
Gustavo Campos


2009/3/24 Er Galvao Abbott <gal...@phpbr.com.br>

Celso

unread,
Mar 24, 2009, 3:18:08 PM3/24/09
to php-brasil
:( Infelizmente o banco "desta" aplicação é o MS SQL...


Vou tentar a solução do Galvão tb.

Valew

On 24 mar, 15:33, "Roberto C. Silva" <beto...@gmail.com> wrote:
> Tenta gerar o arquivo pelo banco de dados.
> se for o mysql ficaria mais ou menos assim
>
> select concat("'", campo1, "'; '", campo2,"'") into outfile
> '/tmp/arquivo.csv' from tabela;
> isto seria uma forma.
> []s
>
> 2009/3/24 Celso <cels...@gmail.com>
>
>
>
> > Olá Pessoal!
> > Estou com um probleminha:
> > Preciso gerar um csv com umas 300 mil linhas ! Já tentamos várias
> > técnicas, mas infelizmente nada: A memória não aguenta...
> > Nós fazemos uma iteração de um array, pra cada iteração damos um fwrite
> > (string...)
>
> > Desde já, grato
> > Celso.
>
> --
> Roberto C Silva

Walker de Alencar

unread,
Mar 25, 2009, 7:54:49 AM3/25/09
to php-brasil
Gere o arquivo por fases, "paginando" a geração.

Faça um redirect para nova página que processará os proximos XXXX
registros.

Processo:
1. Recebe a informação da pagina, se pagina=null, seta 0(zero) ->
2. multiplique a pagina por XXXX registros, use filtro de limitação
(LIMIT no MySQL e ROWS no Oracle) iniciando em (XXXX * pagina),
trazendo XXXX registros. ->
3. varre a lista de registro adicionando(append) linhas no arquivo CSV
->
4. disponibiliza link ou projeta um redirecionamento para próxima
página(pagina+1), lembrando que a qtdPaginas = ceil(totalRegistros /
XXXX)


Walker de Alencar
Arquiteto Web/PHP
http://www.walkeralencar.com
(62) 8172-5487

Vinícius Daniel Antunes Oliveira

unread,
Mar 24, 2009, 9:38:30 PM3/24/09
to php-b...@googlegroups.com
A ideia de fazer de pouco em pouco é o que vai te ajudar realmente. Tambem evite coisas como ficar concatenando a string para depois gravar:

for ($i = 0; i < 90000;++i)
{
$t += "32;jose;13/1/1978";
}
fwrite($file, $t);

faça direto dentro do seu loop e use fflush()

for ($i = 0; i < 90000;++i)
{
$t = "32;jose;13/1/1978";
fwrite($file, $t);
fflush($file);
}



2009/3/24 Celso <cel...@gmail.com>



--
Vinícius Daniel Antunes Oliveira

Erick Belluci Tedeschi

unread,
Mar 25, 2009, 1:26:35 PM3/25/09
to php-brasil
Pessoal,
me corrijam se eu entendi errado:

Como exemplo tomarei o seguinte trecho de código:
<?php
//...

while ($campos = mysql_fetch_assoc($result)) {
$linhaCSV = implode(";", $campos);
fwrite($fp, $campos);
}
?>
No exemplo acima independente de quantas linhas de resultado o SQL
trazer, a gravação do arquivo é por meio de stream de texto, ou seja,
a cada iteração o arquivo no HD é escrito/atualizado e o array sempre
continua do mesmo tamanho.
No ambiente WEB é importante ressaltar que o tempo de execução da
consulta SQL, não pode exceder o tempo limite de execução do script
PHP, que é configurado através da diretiva max_execution_time no
php.ini. Se não o arquivo não será criado por completo.

Trabalho com arquivos de texto de 6GB provenientes de Log de proxy.
Utilizo PHP para gerar algumas estatísticas de acesso, e não tenho
problemas com limite de memória.

Celso,
talvez seja interessante passar para o pessoal o trecho do código
correspondente a esse problema para melhor entendimento da causa do
erro.

Obrigado, t+

On Mar 24, 10:38 pm, Vinícius Daniel Antunes Oliveira
<viniciusdan...@gmail.com> wrote:
> A ideia de fazer de pouco em pouco é o que vai te ajudar realmente. Tambem
> evite coisas como ficar concatenando a string para depois gravar:
>
> for ($i = 0; i < 90000;++i)
> {
> $t *+= *"32;jose;13/1/1978";}
>
> fwrite($file, $t);
>
> faça direto dentro do seu loop e use fflush()
>
> for ($i = 0; i < 90000;++i)
> {
> $t = "32;jose;13/1/1978";
> fwrite($file, $t);
> *fflush($file);*
>  }
>
> 2009/3/24 Celso <cels...@gmail.com>

Ninetails - Carlos Kazuo

unread,
Mar 27, 2009, 12:37:46 AM3/27/09
to php-b...@googlegroups.com
quando vc realiza um select, o banco processa depois te devolve toda a
rowset. Geralmente os bancos de dados trabalham assim. Logo se vc pede
uma rowset muito grande, tem que ter muita memória para aguentar todo
o rowset que vier.

tente dividir sua query...
--------------------------------------------------
Carlos Kazuo - Ninetails
ninetails.nine [at] gmail.com



2009/3/25 Erick Belluci Tedeschi <eric...@gmail.com>:

Erick Belluci Tedeschi

unread,
Mar 27, 2009, 9:08:38 AM3/27/09
to php-brasil
Do proprio manual do PHP:
Função: mysql_fetch_assoc
Descrição: Retorna uma matriz associativa que corresponde a linha
obtida e move o ponteiro interno dos dados adiante.

Olhando a API da Zend vi que o banco não trabalha assim. Funções como
mysql_fetch_assoc, mysql_fetch_object, mysql_fetch_array e
mysql_fetch_row fazem uma chamada para uma função interna da API
chamada php_mysql_fetch_hash que retorna apenas uma linha do SGBD.
Movendo o ponteiro do mysql_result para a próxima linha. Até o
ponteiro virar NULL.

Por isso que pedi para o Celso, mandar um trecho do código dele para
ver quais funções ele está utilizando e como ele está implementando.
Se não nós vamos ficar dando 'pitaco' tentando resolver o problema e
não vamos chegar na resolução.

Abçs, t+


On Mar 27, 1:37 am, Ninetails - Carlos Kazuo
<ninetails.n...@gmail.com> wrote:
> quando vc realiza um select, o banco processa depois te devolve toda a
> rowset. Geralmente os bancos de dados trabalham assim. Logo se vc pede
> uma rowset muito grande, tem que ter muita memória para aguentar todo
> o rowset que vier.
>
> tente dividir sua query...
> --------------------------------------------------
> Carlos Kazuo - Ninetails
> ninetails.nine [at] gmail.com
>
> 2009/3/25 Erick Belluci Tedeschi <erickb...@gmail.com>:

Celso

unread,
Mar 27, 2009, 10:22:30 AM3/27/09
to php-brasil
Nos usamos o CAKEPHP e, tivemos que mudar o fonte deles para retirar
uns "espaços vazios":
No dbo_mssql.php (linha 632):

//We MODIFIED -- TRIM
$resultRow[$table][$column] = trim($row[$index]);

O cara que monta a CSV é este:


$arq = fopen($path.$filename, "w");


$cabecalho = "NOME;" .
"MARCA;" .
"MODELO;" .
"BLA BLA";

fwrite($arq, $cabecalho."\n");
//echo $cabecalho."\n";

foreach ($veiculos as $veiculo ):

$str = $veiculo['Veiculo']['Nome'].";".
$veiculo['Veiculo']['RENAVAM'].";".
$veiculo['MarcaModelo']['DESCRI_MARCA_MODELO'].";".
$veiculo['Veiculo']['BLABLA'];

fwrite($arq, $str."\n");

endforeach;

fclose($arq);


Só em tirar o espaços nós ganhamos bastante !!!

Erick Belluci Tedeschi

unread,
Mar 27, 2009, 10:32:40 AM3/27/09
to php-brasil
Olá Celso,

qual é a parte onde o $veiculos é criado?
O problema do seu script deve estar nessa parte. Que pelo visto, você
está passando todas as linhas para um array e depois para o arquivo.
Acredito que fazendo uma alteração no script, ao invés de mandar as
linhas do resultado do banco de dados para o array, mandar direto para
o arquivo. E assim não terá mais problema de memória.

Att,
Reply all
Reply to author
Forward
0 new messages