Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

sakamoto

importar csv gigante

Recommended Posts

Olá Senhores,

 

Tenho uma determinada tabela com 10 colunas, preciso atualizar 01 coluna (estoque). Meu csv tem mais de 90.000 linhas contendo colunaID e colunaEstoque, preciso atualizar o que já existe (se o id existir ele altera a informacao do estoque) ou insere nova linha caso não encontre o id... como posso proceder pois o arquivo é muito pesado. Me informaram para fazer pausadamente... mas como fazer isso?1

 

Obrigado

Compartilhar este post


Link para o post
Compartilhar em outros sites

Perdão senhores, segue código usado atualmente (lembrando que ele funciona, consegui importar 500 linhas)...

 

<?php
include('../Lib/PHP/config.php');


$Importacao = array(
	'Email' => 'email@cliente.com.br', // email para onde vai o relatorio
	'Diretorio' => 'importar_estoque', // diretorio onde tem o arquivo a ser importado
	'DiretorioLog' => 'relatorios', // diretorio onde será salvo o relatorio
	'Tabela' => 'Produtos', // tabela que será utilizada
	'SeparadorColunas' => ';', // tipo de separador
	'Colunas' => array(
		'ProdutoID',
		'Estoque',
	), // colunas a serem trabalhadas
);



$ponteiro  = opendir($Importacao["Diretorio"]);
$itens = array();

// monta os vetores com os itens encontrados na pasta
while ($nome_itens = readdir($ponteiro)) {
	if (($nome_itens != '.') && ($nome_itens != '..')) {
		$itens[] = $nome_itens;
	}
}

$IMPORTAR = array();

//Le e formata o arquivo para efetuar a importação dos dados
foreach ($itens as $nome_arquivo) {
	if ($ARQUIVO = fopen($Importacao["Diretorio"] . '/' . $nome_arquivo, "r")) {
		$IMPORTAR[$nome_arquivo] = array(
			'Importar' => array(),
			'Erros' => array(),
		);

		//Percorre todas as linhas do arquivo
		while(!feof($ARQUIVO))
		{
			//Recebe o conteudo da linha atual
			if($CONTEUDO = fgets($ARQUIVO))
			{
				//Separa as colunas da linha atual - utiliza a variavel de configuracao acima para saber qual caracter esta usando
				$CONTEUDOEXP = explode($Importacao["SeparadorColunas"],$CONTEUDO);

				//Se o tamanho do array de configuracao onde temos as colunas a serem atualizadas for igual ao total de colunas na linha do arquivo ele efetua os procedimentos de inclusao/alteracao, senao ele armazena o numero da linha com problemas para ao final informar o usuario
				if(sizeof($Importacao["Colunas"])==sizeof($CONTEUDOEXP))
				{
					$count = 0;
					$linha = array();
					$error = FALSE;

					foreach ($Importacao["Colunas"] as $coluna) {
						$linha[$coluna] = $CONTEUDOEXP[$count];

						if (($coluna == 'ProdutoID') || ($coluna == 'GrupoID') || ($coluna == 'Cat') || ($coluna == 'SubCategoriaID') || ($coluna == 'Cat_SUB_SUB') || ($coluna == 'Cat_SUB_SUB_SUB') || ($coluna == 'MarcaID')) {
							$linha[$coluna] = (int)$linha[$coluna];

							if (($coluna == 'ProdutoID') && ($linha[$coluna] <= 0)) {
								$IMPORTAR[$nome_arquivo]['Erros'][] = array('Linha' => $CONTEUDO, 'Motivo' => 'ProdutoID inválido.');
								$error = TRUE;
								break;
							}
						}
						elseif (($coluna == 'DataCriacao') || ($coluna == 'DataAlteracao')) {
							list($dia, $mes, $ano) = explode('/', $linha[$coluna]);
							$temp = $mes . '/' . $dia . '/' . $ano;
							$linha[$coluna] = date('Y-m-d', strtotime($temp)) . ' 00:00:00';

							unset($dia, $mes, $ano, $temp);
						}
						elseif ($coluna == 'Voltagem') {
							$linha[$coluna] = empty($linha[$coluna]) ? 'N' : $linha[$coluna];
						}
						elseif (($coluna == 'Valor') || ($coluna == 'ValorDesconto')) {
							$linha[$coluna] = (float)$linha[$coluna];
							$linha[$coluna] = number_format($linha[$coluna], 2, '.', '');
						}

						$count++;
					}

					if (!$error) {
						$IMPORTAR[$nome_arquivo]['Importar'][] = array('Conteudo' => $CONTEUDO, 'Dados' => $linha);
					}
				}
				else {
					$IMPORTAR[$nome_arquivo]['Erros'][] = array('Linha' => $CONTEUDO, 'Motivo' => 'Numero de colunas da linha diferente das da tabela no banco.');
				}
			}
		}

		fclose($ARQUIVO);
	}
}

//Le o array com infomacoes para importação, importa e gera arquivo de log.
if (sizeof($IMPORTAR) > 0) {
	foreach ($IMPORTAR as $NOME_ARQUIVO => $DADOS) {
		if (sizeof($DADOS['Importar']) > 0) {
			foreach ($DADOS['Importar'] as $KEY_CONTEUDO => $CONTEUDO) {
				$SQL_UPDATE_WHERE = $Importacao["Colunas"][0] . "='" . recebe_dados($CONTEUDO['Dados'][$Importacao["Colunas"][0]]) . "'";

				$SQL_CAMPOS_INSERT = "";
				$SQL_VALUES_INSERT = "";
				$SQL_UPDATE_VALUES = "";
				$cont=-1;

				foreach ($CONTEUDO['Dados'] as $COLUNA => $VALOR) {
					$cont++;
					$SQL_CAMPOS_INSERT .= $COLUNA;
					$SQL_CAMPOS_INSERT .= ($cont < (sizeof($CONTEUDO['Dados'])-1)) ? "," : "";
					$SQL_VALUES_INSERT .= "'" . recebe_dados($VALOR) . "'";
					$SQL_VALUES_INSERT .= ($cont < (sizeof($CONTEUDO['Dados'])-1)) ? "," : "";
					$SQL_UPDATE_VALUES .= $COLUNA . "='" . recebe_dados($VALOR) . "'";
					$SQL_UPDATE_VALUES .= ($cont < (sizeof($CONTEUDO['Dados'])-1)) ? ", " : "";
				}

				$SELECT = "SELECT 1 FROM ".$Importacao["Tabela"]." WHERE ".$SQL_UPDATE_WHERE;
				$RES = mysql_query($SELECT);

				if (!$RES) {
					$IMPORTAR[$NOME_ARQUIVO]['Erros'][] = array('Linha' => $CONTEUDO['Conteudo'], 'Motivo' => 'ProdutoID inválido.');
					unset($IMPORTAR[$NOME_ARQUIVO]['Importar'][$KEY_CONTEUDO]);
				}
				else {
					if (mysql_num_rows($RES) > 0)
					{
						$SELECT = "UPDATE ".$Importacao["Tabela"]." SET ".$SQL_UPDATE_VALUES." WHERE ".$SQL_UPDATE_WHERE;
						$RES = mysql_query($SELECT);
					}
					else
					{
						$SELECT = "INSERT INTO ".$Importacao["Tabela"]."(".$SQL_CAMPOS_INSERT.") VALUES(".$SQL_VALUES_INSERT.")";
						$RES = mysql_query($SELECT);
					}

					if (!$RES) {
						$IMPORTAR[$NOME_ARQUIVO]['Erros'][] = array('Linha' => $CONTEUDO['Conteudo'], 'Motivo' => 'Erro SQL ## Comando -> ' . $SELECT . ' ## Resposta -> ' . mysql_error() . '.');
						unset($IMPORTAR[$NOME_ARQUIVO]['Importar'][$KEY_CONTEUDO]);
					}
				}
			}
		}
	}

	foreach ($IMPORTAR as $NOME_ARQUIVO_ => $DADOS) {
		$NOME_ARQUIVO = str_replace('.', '_', $NOME_ARQUIVO_) . '__LOGIMPORT__' . date('Y-m-d_H-i-s') . '.txt';
		$quebra = chr(13).chr(10);

		//Gera conteudo para o arquivo de Log
		$CONTEUDO_ARQUIVO_LOG = 'Arquivo: ' . $NOME_ARQUIVO_ . $quebra;
		$CONTEUDO_ARQUIVO_LOG .= '===============================================' . $quebra;

		if (sizeof($DADOS['Importar']) > 0) {
			$CONTEUDO_ARQUIVO_LOG .= 'Registros importados com sucesso: ' . sizeof($DADOS['Importar']) . $quebra;
		}

		$CONTEUDO_ARQUIVO_LOG .= (sizeof($DADOS['Erros']) > 0) ? 'Registros não importados: ' . sizeof($DADOS['Erros']) . $quebra : 'Nenhum erro encontrado.' . $quebra;

		$CONTEUDO_ARQUIVO_LOG .= '===============================================' . $quebra . $quebra;

		foreach ($DADOS['Erros'] as $DADOS_LINHA) {
			$CONTEUDO_ARQUIVO_LOG .= 'Linha => ' . $DADOS_LINHA['Linha'] . $quebra;
			$CONTEUDO_ARQUIVO_LOG .= 'Motivo => ' . $DADOS_LINHA['Motivo'] . $quebra . $quebra . '--' . $quebra . $quebra;
		}

		//Cria arquivo de log no servidor
		$fp = fopen($Importacao['DiretorioLog'] . '/' . $NOME_ARQUIVO, 'a');
		$escreve = fwrite($fp, $CONTEUDO_ARQUIVO_LOG);
		fclose($fp);

		//Envia email com log
		if (!empty($Importacao['Email'])) {
			$emailsender = (eregi('tempsite.ws$|locaweb.com.br$|hospedagemdesites.ws$|websiteseguro.com$', $_SERVER[HTTP_HOST])) ? 'webmaster@cliente.com.br' : "webmaster@" . $_SERVER[HTTP_HOST];
			$quebra_linha = (PATH_SEPARATOR == ";") ? "\r\n" : "\n";
			$nomeremetente = "Cliente";
			$emailremetente = $emailsender;

			$comcopia          = $Importacao['Email'];
			$assunto           = "Importação de dados - " . $NOME_ARQUIVO;

			$headers = "MIME-Version: 1.1" .$quebra_linha;
			$headers .= "Content-type: text/html; charset=iso-8859-1" .$quebra_linha;
			$headers .= "From: ".$nomeremetente ." <" . $emailremetente.">".$quebra_linha;
			$headers .= "Cc: " . $comcopia . $quebra_linha;
			$headers .= "Bcc: " . $comcopiaoculta . $quebra_linha;
			$headers .= "Reply-To: " . $emailremetente . $quebra_linha;

			if (!mail($Importacao['Email'], $assunto, str_replace($quebra, '<br>', $CONTEUDO_ARQUIVO_LOG), $headers ,"-r".$emailsender)) { // Se for Postfix
				$headers .= "Return-Path: " . $emailsender . $quebra_linha; // Se "não for Postfix"
				mail($Importacao['Email'], $assunto, str_replace($quebra, '<br>', $CONTEUDO_ARQUIVO_LOG), $headers);
			}
		}

		unlink($Importacao["Diretorio"] . '/' . $NOME_ARQUIVO_);
	}

	print 'Dados importados - Revise os arquivos de log para visualização de possiveis erros!';
}
else {
	print 'Não existem arquivos pendentes para importação!';
}
?>

Compartilhar este post


Link para o post
Compartilhar em outros sites

Você não consegue importar mais por falta de memória? Tente aumentar o limite no seu arquivo php.ini ou parta o arquivo em várias partes (mas você terá que se certificar de que tanto a linha inicial quanto a final estejam inteiras nas várias partes) usando um programa específico, como o JJSplit.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então mangakah obrigado por se mostrar a ajudar... exatamente o arquivo não importa por conta de memória... já tentei aumentar o php.ini para o tamanho do arquivo para 10mb. Não posso picar o arquivo pois o cliente fará upload do arquivo a ser importado na pasta e rodará o importar_estoque.php. Minha idéia seria importar uma certa quantidade, pausar, importar mais, pausar etc... não sei se isso vai aliviar a memória... mas também nem imagino como fazê-lo. Você tem alguma outra idéia?!?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então mangakah obrigado por se mostrar a ajudar... exatamente o arquivo não importa por conta de memória... já tentei aumentar o php.ini para o tamanho do arquivo para 10mb. Não posso picar o arquivo pois o cliente fará upload do arquivo a ser importado na pasta e rodará o importar_estoque.php. Minha idéia seria importar uma certa quantidade, pausar, importar mais, pausar etc... não sei se isso vai aliviar a memória... mas também nem imagino como fazê-lo. Você tem alguma outra idéia?!?

 

Na verdade você já está usando a solução que consome menos memória. Usar fgets() faz o PHP lê o arquivo linha por linha até a conclusão, ao contrário de file_get_contents() que carrega de uma vez todo o conteúdo de um arquivo. É bom especificar o tamanho máximo de cada linha (o segundo parâmetro da fumção fgets), só porque você não especificou um não significa que não haja um limite.

 

Verifique também o tempo máximo de execução (diretiva 'max_execution_time' no php.ini), o padrão é de apenas 30 segundos. Se possível, amplie isso também.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Outra coisa que pode ajudar...

 

Você tem vários loops no mesmo arquivo logo, na mesma requisição. Se você fragmentar cada etapa em múltiplas requisições, interligando-as através de sessões, a última etapa que é a de inserir/atualizar no banco terá muitomais memória para executar.

 

Se mesmo fragmentando dessa forma ainda não for suficiente, você pode determinar porções menores do array $IMPORTAR através de querystrings as quais preencherão os argumentos de um array_slice()

Compartilhar este post


Link para o post
Compartilhar em outros sites

Você não apenas inserir, vai montar uma "falsa paginação" sobre $IMPORTAR

 

Falsa porque não haverão números a serem clicados. A "página" estará oculta, numa tag META Refresh.

 

Informando o quarto argumento como TRUE, os índices serão preservados. O padrão é FALSE, isto é, cada nova fatia começará a contar do zero.

 

Mantendo os índices originais, a cada nova requisição você pegaria o índice do último elemento da fatia, adicionaria +1 e montaria a tag META Refresh passando esse valor como parâmetro GET.

 

A página seria a mesma, mas a cada nova requisição esse valor mudará, e você conseguirá controlar o argumento offset de array_slice().

 

Na primeira requisição você teria a fatia de 0 à 50 (exemplo). Faz o insert/update e monta a tag meta refresh com o valor 51:

 

$importar = array_slice( $importar, $_GET['offset'], 50, TRUE );

Na segunda requisição, essa mesma linha retornará uma fatia diferente do array, agora de 51 até 101 e variável GET será 102.

 

Na terceira requisição, teremos de 102 até 152 e variável GET será 153.

 

E por aí vai, até acabar.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.