Ir para conteúdo

POWERED BY:

Arquivado

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

anthraxxxxxxx

Consultas no MySQL com PHP demoram demais

Recommended Posts

Não estou com um problema em si, mas estou querendo melhorar o desempenho, no seguinte exemplo, eu faço a consulta na tabela empresas, e com base nas empresas, eu consulto quantos parcelamentos essa empresa possui, mas, além disso, além de contar quantos parcelamentos a empresa possui, eu preciso contar o total de parcelas, então são três camadas de tabelas:

Lista empresas -

 

Empresa -> Parcelamentos -> Parcelas por parcelamento

 

OBS: a mesma empresa pode ter vários parcelamentos.

OBS 2: são quantidades enormes de parcelas, e as parcelas não tem dados fixos, então cada parcela é uma linha no banco de dados.

 

Com o código abaixo, eu faço isso descrito acima (funciona):

 

<?php
            $sql = "SELECT id,cod,empresa_rel,qntd_parc FROM control_parc_fiscal";
            $parcs = Components::get_rs_array_stmt_PDO($sql);
            $emps_cod = array_column($parcs, 'empresa_rel');
            $SQL_emp_cod = implode("','", $emps_cod);
            if ($nivel <= 3) {
                $sql_2 = "SELECT id,cod,nome,cnpj,insc_estadual,tributacao FROM empresas_base WHERE cod IN ('$SQL_emp_cod') AND responsavel = '$user' GROUP BY id ORDER BY id";
            } else {
                $sql_2 = "SELECT id,cod,nome,cnpj,insc_estadual,tributacao FROM empresas_base WHERE cod IN ('$SQL_emp_cod')";
            }
            $emps = Components::get_rs_array_stmt_PDO($sql_2);
            $countParcs = Components::get_count_reg_mysql_PDO($sql_2);
            $i = 0;
            foreach ($emps as $emps_loop) {
                $emps_data[$i]['id'] = $emps_loop['id'];
                $emps_data[$i]['cod'] = $emps_loop['cod'];
                $emps_data[$i]['nome'] = $emps_loop['nome'];
                $emps_data[$i]['cnpj'] = $emps_loop['cnpj'];
                $emps_data[$i]['insc_estadual'] = $emps_loop['insc_estadual'];
                $b = 0;
                foreach ($parcs as $parc) {
                    $cod[$b] = $parc['empresa_rel'];
                    $control_cod[$b] = $parc['cod'];
                    if ($emps_data[$i]['cod'] == $cod[$b]) {
                        $a = 0;
                        while ($a <= $b) {
                            if ($emps_data[$i]['cod'] == $cod[$b]) {


                                $emps_data[$i]['parcs'][$b] = Array(
                                    "parc_cod" => $parc['cod'],
                                    "qntd_parc" => $parc['qntd_parc'],
                                    "count_venc" => $countVencParcs
                                );
                            } else {
                                $emps_data[$i]['parcs'] = NULL;
                            }
                            $a++;
                        }
                        $b++;
                    }
                }
                //echo $countVencParcs;
                $i++;
            }

            for ($p = 0; $p < count($emps); $p++) {
                $empresaID = $emps_data[$p]['id'];
                $empresaCod = $emps_data[$p]['cod'];
                $empresa = utf8_encode($emps_data[$p]['nome']);
                $cnpj = $emps_data[$p]['cnpj'];
                $insc_est = $emps_data[$p]['insc_estadual'];
                $grupo_easy = $emps_data[$p]['grupo_easy'];
                $regime_trib = $emps_data[$p]['tributacao'];

                $countParcs = count($emps_data[$p]['parcs']);
               //O que está fazendo a consulta ficar lenta é apenas está função
                $qntdVencidas = fiscalModel::get_count_parc_venc_parcelamento_fiscal_por_emp($empresaCod);
                if ($qntdVencidas['qntd_parcs_venc'] > 0) {
                    $tr_classes = 'danger';
                    $vnctMsg = " - Esta empresa possui " . $qntdVencidas['qntd_parcs_venc'] . " parcela(s) vencida(s)";
                } else{
                    $vnctMsg = "";
                    $tr_classes = 'default';
                }
                ?>
            <tr class="<?php echo $tr_classes ?>">
                    <td>
                        <a href="#" class='select_emp_parcelamento_fiscal' data-empresa="<?php echo $empresaCod ?>"><i class="fa fa-plus-circle"></i></a>
                    </td>
                    <td class='td_empresa'>
                        <?php
                        echo $empresa;
                        echo $vnctMsg;
                        ?>
                    </td>
                    <td>
                        <?php echo $cnpj ?>
                    </td>
                    <td>
                        <?php echo $insc_est ?>
                    </td>
                    <td>
                        <?php
                        echo $countParcs;
                        ?>
                    </td>
                </tr>
                <?php
            }
            ?>

Esta linha :

 

Citar

$qntdVencidas = fiscalModel::get_count_parc_venc_parcelamento_fiscal_por_emp($empresaCod);

 

É responsável por contar as parcelas vencidas, ela faz uma consulta, em média 5 vezes por empresa, em cerca de 60 campos, para cada uma das 311 empresas cadastradas até o momento, ou seja, são 90.000 registros.

 

Esse código abaixo, é o código executado por essa função

<?php
public static function get_count_parc_venc_parcelamento_fiscal_por_emp($emp) {

        $query = "SELECT control_rel,data_venc FROM control_parc_fiscal_parcelas WHERE data_pgto = ''";
        $rs = Components::get_rs_consulta_query($query);
        $countVencParcs = 0;
        $i = 0;
        $hoje = date('Y-m-d');
        while ($row = mysqli_fetch_array($rs)) {

            $control_rel = $row['control_rel'];
            $vnct = $row['data_venc'];
            $data_inicial = Components::exec_format_date($vnct, 'd/m/Y', 'Y-m-d');
            $verif_venc = Components::calc_diff_date_f($data_inicial, $hoje);
            $sql_2 = "SELECT cod,empresa_rel FROM control_parc_fiscal WHERE cod = '$control_rel' AND empresa_rel = '$emp'";
            $rs_2 = Components::get_rs_consulta_query($sql_2);
            $row_2 = mysqli_fetch_array($rs_2);
            $empresa = $row_2['empresa_rel'];
            $cod = $row_2['cod'];
            if ($control_rel == $cod) {
                if ($verif_venc > 0) {
                    $countVencParcs += 1;
                } else {
                    $countVencParcs += 0;
                }
            } else {
                
            }
            $i++;
        }

        $return = Array(
            "empresa" => $empresa,
            "qntd_parcs_venc" => $countVencParcs
        );

        return $return;
    }
?>

 

Não tenho ideia de como agilizar isso, se alguém tiver uma luz do que eu deva pesquisar, sabendo que esse é o famoso problema do 'n+1', mas não consigo achar outra solução, ou seria realmente viável armazenar todos esses dados em um array ?

 

Obrigado

Compartilhar este post


Link para o post
Compartilhar em outros sites

Chegou a dar uma lida nisso?

http://rberaldo.com.br/o-problema-do-n-mais-1/

 

Você poderia armazenar todos as chaves q vai usar e efectuar apenas uma carga.

Atacar o problema com VIEWS para abstrair consultas complexas.

 

As vezes pode ser índice na tabela tmb (estamos falando de qnt registros?)

 

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites
Agora, gabrieldarezzo disse:

Chegou a dar uma lida nisso?

http://rberaldo.com.br/o-problema-do-n-mais-1/

 

Você poderia armazenar todos as chaves q vai usar e efectuar apenas uma carga.

Atacar o problema com VIEWS para abstrair consultas complexas.

 

As vezes pode ser índice na tabela tmb (estamos falando de qnt registros?)

 

 

 

 

Sim sim, pode ver que a primeira parte das consultas está livre desse problema.

 

Estamos falando de 90.000 registros agora, mas a quantidade é crescente e em larga escala.

 

No caso, seria viável eu criar mais um indice de array pras parcelas ?

 

Seria algo tipo


 

<?php

$array[0] = Array(

    'empresa' => 'empresa 1',
	'parcelamentos da empresa 1' => Array(
        [0] => Array(
    		'parcelamento 1' => 'Parcelamento 1 da empresa 1',
      			'parcelas' => Array(
        		[0] => Array(
                  'parcela' => 'parcela 1, do parcelamento 1, da empresa 1',  
                  )
                [1] => Array(
                  'parcela' => 'parcela 2, do parcelamento 1, da empresa 1',  
                  )
                )	
    		),
       [1] => Array(
    		'parcelamento 2' => 'Parcelamento 2 da empresa 1',
      			'parcelas' => Array(
        		        [0] => Array(
                  			'parcela' => 'parcela 1, do parcelamento 2, da empresa 1',  
                  		)
                		[1] => Array(
                  			'parcela' => 'parcela 2, do parcelamento 2, da empresa 1',  
                  		)
        		 )	
    		)
      )	
)
?>

Mas não teria perigo de crashar o navegador com tantos dados ?, estou tentando de tudo

Compartilhar este post


Link para o post
Compartilhar em outros sites

O navegador provavelmente vai travar se tentar exibir os 90k de 1 vez.

 

Eu fui seco no titulo 'mysql lento'

Mas a lentidão não está no servidor e sim na renderização?

 

Ou realmente oq está lento é o navegador?

Nesse caso você pode tentar abordagens como paginação de resultados.

 

Outro fator, qnd citei indice seria indice do MYSQL:

http://www.linhadecodigo.com.br/artigo/3620/indices-mysql-otimizacao-de-consultas.aspx

https://dev.mysql.com/doc/refman/5.5/en/mysql-indexes.html

 

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites
16 minutos atrás, gabrieldarezzo disse:

O navegador provavelmente vai travar se tentar exibir os 90k de 1 vez.

 

Eu fui seco no titulo 'mysql lento'

Mas a lentidão não está no servidor e sim na renderização?

 

Ou realmente oq está lento é o navegador?

Nesse caso você pode tentar abordagens como paginação de resultados.

 

Outro fator, qnd citei indice seria indice do MYSQL:

http://www.linhadecodigo.com.br/artigo/3620/indices-mysql-otimizacao-de-consultas.aspx

https://dev.mysql.com/doc/refman/5.5/en/mysql-indexes.html

 

 

 

 

A lentidão está sim no servidor, ele está em uma função especifica, que precisa contar quantas parcelas estão vencidas, só pra saber quais estão vencidas, estou passando pelos 90.000 registros em todas as vezes que eu passo por um parcelamento, e quando está vencida, somo +1 e acrescento ao parcelamento relacionado.

 

No caso, é essa função:

 

<?php
public static function get_count_parc_venc_parcelamento_fiscal_por_emp($emp) {

        $query = "SELECT control_rel,data_venc FROM control_parc_fiscal_parcelas WHERE data_pgto = ''";
        $rs = Components::get_rs_consulta_query($query);
        $countVencParcs = 0;
        $i = 0;
        $hoje = date('Y-m-d');
        while ($row = mysqli_fetch_array($rs)) {

            $control_rel = $row['control_rel'];
            $vnct = $row['data_venc'];
            $data_inicial = Components::exec_format_date($vnct, 'd/m/Y', 'Y-m-d');
            $verif_venc = Components::calc_diff_date_f($data_inicial, $hoje);
            $sql_2 = "SELECT cod,empresa_rel FROM control_parc_fiscal WHERE cod = '$control_rel' AND empresa_rel = '$emp'";
            $rs_2 = Components::get_rs_consulta_query($sql_2);
            $row_2 = mysqli_fetch_array($rs_2);
            $empresa = $row_2['empresa_rel'];
            $cod = $row_2['cod'];
            if ($control_rel == $cod) {
                if ($verif_venc > 0) {
                    $countVencParcs += 1;
                } else {
                    $countVencParcs += 0;
                }
            } else {
                
            }
            $i++;
        }

        $return = Array(
            "empresa" => $empresa,
            "qntd_parcs_venc" => $countVencParcs
        );

        return $return;
    }
?>

Essa função está dentro do problema do 'N + 1' porque ela é chamada dentro do loop dos parcelamentos da array, ou seja, ela é chamada uma quantidade gigantesca de vezes, e essa função que é executada 90.000 vezes fazendo a consulta ficar lenta (e cada dia aumenta mais a quantidade de registros pois tem gente utilizando o sistema).

 

Com essa função o tempo de resposta é 12347ms.

Sem essa função o tempo de resposta é 32ms.

 

Mas eu preciso do famigerado dado que ela me traz.

 

Obs: As 3 tabelas relacionadas estão indexadas, mudar de banco de dados seria uma solução plausível, sabendo que provavelmente ao longo o tempo boa parte do sistema pode ir entrando nesse mesmo problema?

Ou o MySQL com PHP comportam, desde que programados corretamente, uma quantidade massiva de dados e consultas gigantescas (mantendo um bom desempenho)?

 

PS:

 

@gabrieldarezzoValeu por responder quase todas as minhas perguntas kkkk

 

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom olhando com calma

Independente de pensar em N+1 ou Joins 

 

Tem coisas redundantes no seu código sem a necessidade.

 

Ex, a função get_count_parc_venc_parcelamento_fiscal_por_emp():

 

Atualmente ela executa isso aqui:

$query = "SELECT control_rel,data_venc FROM control_parc_fiscal_parcelas WHERE data_pgto = ''";

Sendo que os dados nunca se alteram.


Você poderia executar eles fora dos loops e apenas enviar o resultado dela como parâmetro, algo +/- assim:

public static function get_count_parc_venc_parcelamento_fiscal_por_emp($emp, $control_parc_fiscal_parcelas) {
    
    ...
    $hoje = date('Y-m-d');
    foreach($control_parc_fiscal_parcelas as $row){
      .....
 

Ps: Nomear as variáveis ajuda você e terceiros e você mesmo a ter um código mais simples....

Ex: oq significa $row? É a linha... ok mas linha do que? preferia algo como:

foreach($parcelas as $parcela)

 

 

 

Falando em Joins você sabe o conceito?
Olhei por cima... mas acho q da pra fazer quase tudo com Joins ;)

https://www.w3schools.com/sql/sql_join_inner.asp

 

Posta a estrutura das tabelas se tiver tentando e estiver com dificuldade.

 

Compartilhar este post


Link para o post
Compartilhar em outros sites
18 minutos atrás, gabrieldarezzo disse:

Bom olhando com calma

Independente de pensar em N+1 ou Joins 

 

Tem coisas redundantes no seu código sem a necessidade.

 

Ex, a função get_count_parc_venc_parcelamento_fiscal_por_emp():

 

Atualmente ela executa isso aqui:


$query = "SELECT control_rel,data_venc FROM control_parc_fiscal_parcelas WHERE data_pgto = ''";

Sendo que os dados nunca se alteram.


Você poderia executar eles fora dos loops e apenas enviar o resultado dela como parâmetro, algo +/- assim:


public static function get_count_parc_venc_parcelamento_fiscal_por_emp($emp, $control_parc_fiscal_parcelas) {
    
    ...
    $hoje = date('Y-m-d');
    foreach($control_parc_fiscal_parcelas as $row){
      .....
 

Ps: Nomear as variáveis ajuda 3° e você mesmo se enteder... oq significa $row é a linha... ok mas linha do que?

 

Falando em Joins você sabe o conceito, to com a cabeça meia quente... mas acho q da pra fazer quase tudo com Joins ;)

https://www.w3schools.com/sql/sql_join_inner.asp

 

Irmão, na hora que você respondeu, eu consegui resolver, usando IN, e associando arrays, o problema estava em que:

 

Onde chamava a função, era na view do sistema, ou seja, ele chama um vez pra cada linha que era printada na tela, bom, isso não existe mais, e essa função não existe mais, o que eu fiz foi transformar a quantidade de parcelas vencidas em um atributo do parcelamento, como isso? (achei muito burro da minha parte não ter pensado nisso antes)

 

<?php
//Esse dado ja vinha do banco em outra consulta
$parcs_cod = array_column($parcs,'cod');
$SQL_parc_cod = implode("','",$parcs_cod);
$hoje = date('Y-m-d');
$sql_3 = "SELECT control_rel,data_venc FROM control_parc_fiscal_parcelas WHERE control_rel IN ("$SQL_parc_cod") AND data_venc < '$hoje'";

$parcs_arr = Components::get_rs_array_stmt_PDO($sql_3);

Isso foi incluso na função que prepara a array com os dados dos parcelamentos, ai dentro dessa mesa função, no momento em que eu abro a array com parcelamento e associo os parcelamentos a uma empresa, eu crio essa outra comparação:

<?php
$f = 0;
foreach($parcs_arr as $parc_diss){
  	//Isso foi trazido pela consulta da query '$sql_3'
	$control_vnct = $parc_diss['control_rel'];
  	//Se o codigo do controle trazida da query '$sql_3' for igual, ao controle que ja está sendo tratado
  	if($ctrl_vnct == $control_cod[$b]){
     	$f += 1; 
    }else{
      $f += 0;
    }
}

Depois disso apenas relaciono o valor de $f para cada relacionamento, e na view, onde eu exibo os dados, somo a quantidade de parcelas vencidas pra cada parcelamento, e pra cada parcelamento somo para as empresas.

 

A consulta reduziu de incríveis mais de 12 segundos, pra 40/60ms. 

 

Realmente...

 

Novamente obrigado @gabrieldarezzo

Compartilhar este post


Link para o post
Compartilhar em outros sites

Boaaa!!!!
Mandou muito.

 

Pra fechar com chave de ouro 
Da uma lida sobre LEFT/INNER joins, as vezes simplifica ainda mais o seu código e não esquece de nomear as variáveis de maneira auto explicativas.

 

Abraços

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

  • Conteúdo Similar

    • Por ILR master
      Tudo bem pessoal?
       
      No código abaixo, estou fazendo uma consulta nas tabelas, banners e banners_referencia
      Meu objetivo é trazer resultados com valores iguais ao nome da cidade declarada na $cidade ou resultados com a referencia Total.
      O problema é que está trazendo todos os resultados. Tenho 10 linhas, 1 com o nome da cidade e duas com o valor Total, então o resultado teria que ser de apenas 3 linhas, mas mostra tudo.
       
      $banner = "SELECT A.*, B.* FROM banners A, banners_referencia B WHERE B.cod_referencia = A.cod_referencia AND A.cidade = '$cidade' OR B.referencia = 'Total' ORDER BY RAND()";
      $banner = mysqli_query($conexao, $banner) or die ("Banner não encontrado");
      while($busca= mysqli_fetch_array($banner)){
          print $busca['cidade'].'<br>';
      };
       
      Alguém consegue me ajudar?
    • Por Rafael_Ferreira
      Não consigo carregar a imagem do captcha do meu formulário. Foi testado com o xampp e easyphp. Também não carregou a imagem de outros captcha. 
       
       
    • Por luiz monteiro
      Olá, tudo bem?
       
      Estou melhorando meu conhecimento em php e mysql e, me deparei com o seguinte. A tabela da base de dados tem um campo do tipo varchar(8) o qual armazena números. Eu não posso alterar o tipo desse campo. O que preciso é fazer um select para retornar o números que contenham zeros a direita ou a esquerda.
      O que tentei até agora
       
      Ex1
      $busca = $conexao->prepare("select campo form tabela where (campo = :campo) ");
      $busca->bindParam('campo', $_REQUEST['campo_form']);
       
      Se a direita da string $_REQUEST['campo_form'] termina ou inicia com zero ou zeros, a busca retorna vazio.
      Inseri dados numéricos, da seguinte maneira para testar: 01234567;  12345670: 12345678: 12340000... entre outros nessa coluna. Todos os valores que não terminam ou não iniciam com zero ou zeros, o select funciona.
       
       
      Ex2
      $busca = $conexao->prepare("select campo form tabela where (campo = 0340000) ");
      Esse número está cadastrado, mas não retorna.
       
      Ex3
      $busca = $conexao->prepare("select campo form tabela where (campo = '02340001' ) ");
      Esse número está cadastrado, mas não retorna.
       
       
      Ex4
      $busca = $conexao->prepare("select campo form tabela where (campo like 2340000) ");
      Esse número está cadastrado, mas não retorna.
       
      Ex5
      $busca = $conexao->prepare("select campo form tabela where (campo like '12340000') ");
      Esse número está cadastrado, mas não retorna.
       
      Ex6
      $busca = $conexao->prepare("select campo form tabela where (campo like '"12340000"' ) ");
      Esse número está cadastrado, mas não retorna.
       
       
      Ex7
      $busca = $conexao->prepare("select campo form tabela where (campo like :campo) ");
      $busca->bindParam('campo', $_REQUEST['campo_form'])
      Não retorna dados.
       
      O  $_REQUEST['campo_form'] é envio via AJAX de um formulário. 
      Usei o gettype para verificar o post, e ele retorna string.
      Fiz uma busca com número 12345678 para verificar o que o select retorna, e também retrona como string.
       
      Esse tipo de varchar foi usado porque os números que serão gravados nesse campo,  terão zeros a direita ou na esquerda. Os tipos number do mysql não gravam zeros, então estou usando esse. O problema é a busca.
      Agradeço desde já.
       
       
    • Por daemon
      Boa tarde,
       
      Eu tenho uma rotina que faz uma leitura do arquivo .xml de vários sites.

      Eu consigo pegar o tópico e a descrição, e mostrar a imagem que esta na pagina do link.
      Para isso utilizo esta função:
      function getPreviewImage($url) { // Obter o conteúdo da página $html = file_get_contents($url); // Criar um novo objeto DOMDocument $doc = new DOMDocument(); @$doc->loadHTML($html); // Procurar pela tag meta og:image $tags = $doc->getElementsByTagName('meta'); foreach ($tags as $tag) { if ($tag->getAttribute('property') == 'og:image') { return $tag->getAttribute('content'); } } // Se não encontrar og:image, procurar pela primeira imagem na página $tags = $doc->getElementsByTagName('img'); if ($tags->length > 0) { return $tags->item(0)->getAttribute('src'); } // Se não encontrar nenhuma imagem, retornar null return null; } // Uso: $url = "https://example.com/article"; $imageUrl = getPreviewImage($url); if ($imageUrl) { echo "<img src='$imageUrl' alt='Preview'>"; } else { echo "Nenhuma imagem encontrada"; }  
      Mas estou com um problema, esta funcão funciona quando coloco em uma pagina de teste.php. Preciso mostrar em uma página inicial diversas fotos de todos os links. (No caso acima só funciona 1).
×

Informação importante

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