Tempo de execução muito longo com ArrayList + MySql com JDBC

[quote=rmendes08]Corrija a query por favor:

CASE WHEN CDSTATUSP IS NOT NULL THEN 1 ELSE 0

rmendes08,

Continua dando pau na SQL.
SQL Utilizada:

SELECT   
   NUINSCPROF, CDCATEGORIA, CDTIPO, CDTAXA,  
   COUNT(NUPARCELA) AS PARCELAS,   
   SUM( CASE WHEN CDSTATUSP IS NOT NULL THEN 1 ELSE 0 ) as PARCELAS_PAGAS  
FROM boletosteste  
GROUP BY NUINSCPROF, CDCATEGORIA, CDTIPO, CDTAXA  

Erro encontrado:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘) as PARCELAS_PAGAS FROM boletosteste GROUP BY NUINSCPROF, CDCATEGORIA, CDTI’ at line 4

Cheguei a tirar o as PARCELAS_PAGAS mas não funcionou amigo!
:confused:

Por desencargo de consciência, testei recuperar os dados por um objeto Serializable convertido para ArrayList e ainda piorou o tempo de resposta… Ou seja, ainda assim é melhor recuperar via SQL direto pro ArrayList, ao invés de salvar da SQL pra um arquivo.ser e então ler sempre dele. SQL Utilizada: (É a mesma que postei pela primeira vez)

SELECT M.CDCATEGORIA, M.NUINSCPROF, M.CDTIPO, M.CDTAXA, M.NUPARCELA, M.CDSTATUSP
FROM MOVBOLETOS M
WHERE M.ANO =2014
AND ( M.CDCATEGORIA LIKE '01' OR M.CDCATEGORIA LIKE '03' OR M.CDCATEGORIA LIKE '04' )
AND M.CDTIPO LIKE  '04'
AND M.NUINSCPROF = ( 
SELECT P.NUINSCPROF
FROM PROFISSIONAIS P
WHERE M.CDCATEGORIA = P.CDCATEGORIA
AND M.CDTIPO = P.CDTIPO
AND M.CDSUBTIPO = P.CDSUBTIPO
AND M.NUINSCPROF = P.NUINSCPROF
AND P.CDSITUACAO =1 ) 
ORDER BY M.CDCATEGORIA, M.NUINSCPROF, M.CDTIPO, M.CDTAXA, M.NUPARCELA ASC 

Resultado dos testes:
SQL->ArrayList:
1ªVEZ: 7,875 segundos (Tempo só na SQL: 6,234 ms)
2ªVEZ: 7,625 segundos (Tempo só na SQL: 6,016 ms)

SQL->Serializable->ArrayList:
1ªVEZ: 25,5 segundos (Tempo só na SQL: 6,234 ms)
2ªVEZ: 10,438 segundos (s/ SQL, direto do arquivo)

Lembro que, o pior de tudo é que totalizar os valores já está complicado pelo que vejo das respostas de vocês e das formas que eu tentei, e vou precisar ainda depois separar em categorias os totais. Ex.:
Enfermeira(o) Geradas:1568 Pagas:537 NãoPagas:931
Técnica(o) Geradas:8079 Pagas:1025 NãoPagas:7054
Auxiliar Geradas:696 Pagas:321 NãoPagas:375
Total Geradas:13343 Pagas:1883 NãoPagas:11860

Faltará o END do case:

SELECT NUINSCPROF, CDCATEGORIA, CDTIPO, CDTAXA, COUNT(NUPARCELA) AS PARCELAS, SUM( CASE WHEN CDSTATUSP IS NOT NULL THEN 1 ELSE 0 END) as PARCELAS_PAGAS FROM boletosteste GROUP BY NUINSCPROF, CDCATEGORIA, CDTIPO, CDTAXA

Antes de mais nada gostaria de agradecer demais a participação de vocês!
Estou adorando meu primeiro contato com a “comunidade” Java, espero que continue assim! :slight_smile:

[quote=pmlm]Faltará o END do case:

SELECT NUINSCPROF, CDCATEGORIA, CDTIPO, CDTAXA, COUNT(NUPARCELA) AS PARCELAS, SUM( CASE WHEN CDSTATUSP IS NOT NULL THEN 1 ELSE 0 END) as PARCELAS_PAGAS FROM boletosteste GROUP BY NUINSCPROF, CDCATEGORIA, CDTIPO, CDTAXA [/quote]

Isso! Agora a SQL funcionou, obrigado!
No entanto a performance ainda está ruim… na primeira execução com a “grande massa de dados” (323.000 registros) demorou 30 segundos, na segunda 4.5 segundos… além do que, não só preciso pegar essas parcelas pagas/não pagas como preciso saber se esse profissional está ativo, consultando na tabela profissionais (94.000 registros). Por isso, a SQL precisa ser mexida para:

SELECT M.NUINSCPROF, M.CDCATEGORIA, M.CDTIPO, M.CDTAXA, COUNT( M.NUPARCELA ) AS PARCELAS, SUM( CASE WHEN M.CDSTATUSP IS NOT NULL THEN 1 ELSE 0 END ) AS PARCELAS_PAGAS
FROM MOVBOLETOS M
WHERE M.NUINSCPROF = (
SELECT P.NUINSCPROF
FROM PROFISSIONAIS P
WHERE P.NUINSCPROF = M.NUINSCPROF
AND M.CDCATEGORIA = P.CDCATEGORIA
AND M.CDTIPO = P.CDTIPO
AND M.CDSUBTIPO = P.CDSUBTIPO )
GROUP BY M.NUINSCPROF, M.CDCATEGORIA, M.CDTIPO, M.CDTAXA

A SQL acima demorou 35 segundos na primeira execução e 3.5 segundos na segunda.
Como se não bastasse gostaria de lembrar que ao final de tudo, precisarei dizer POR CDCATEGORIA qual é o total emitido/pago/não pago … Coisa que devo fazer na programação, certo? Ou não?

Outra coisa, não sei se não tem algum furo na SQL pois na programação que testei diversas vezes em PHP, o total dava em 46343 geradas, e a SQL acima está retornando 47,896 registros…
Lembrando que, a mesma NUINSCPROF pode ter outra CDCATEGORIA também. No caso um profissional pode ter duas “chaves”, pois pode ter um registro de Enfermeiro e de Técnico ao mesmo tempo… No caso 000232-01 e 000232-03 (que devem gerar duas taxas emitidas e não uma)

Errado. Isso deve ser tudo feito via sql. Nada que mais um select com sum e group by não resolva.

O teu problema não é de java, é de Base de Dados: precisas de alguem que olha para as tuas tabelas e as tuas necessidades e veja onde é que pode ser optimizado, criados indices, alteradas queries para outras mais eficazes…

[quote=pmlm]
Errado. Isso deve ser tudo feito via sql. Nada que mais um select com sum e group by não resolva.

O teu problema não é de java, é de Base de Dados: precisas de alguem que olha para as tuas tabelas e as tuas necessidades e veja onde é que pode ser optimizado, criados indices, alteradas queries para outras mais eficazes…[/quote]

Será que criar uma view/procedure não ajudariam meu caso? Nunca fiz isso e nem saberia como chamar um procedimento direto em uma SQL…

pmlm o problema de chaves eu vejo nitidamente, mas como o banco de dados não foi projetado por mim não posso mexer nisso, o que mais dá raiva é de ver que não há uma ID para cada registro desse de profissional ativo. Se fosse por um ID, 4 colunas do banco de dados seriam diminuídas para uma, vezes 323.000 registros… bastante diferença… Já os índices são atribuidos à esses campos usados na consulta, comandos como OPTIMIZE no banco não resolveram…

Mas a consulta não demora taaaanto assim para impedir meu processamento… o problema é que o ArrayList não tá dando conta de receber 323.000 registros e depois manipular 1 a 1…

Pessoal, meu progresso parou por aí.
No final das contas minha conclusão é de que o Java não é tão robusto como falam, pois nada foi dito que resolvesse o problema: consultar Strings (cerca de 46.000) em um ArrayList de objetos (323.000).

Falei com um DBA que me recomendou utilizar materialized view, que nada mais é que: pegar os dados da SQL e jogar em outra tabela, e aí ir atualizando os dados conforme o necessário, no meu caso diariamente…

Vocês tem alguma outra ideia ou concordam que só com a Materialized View meu problema pode ser resolvido?

Abs.

Bom, pessoal, não resolvi 100% o problema, mas melhorei bastante usando o Iterator.

Também vi que fazia muitas verificações desnecessárias, mas ainda assim, sem meu Iterator não desse sempre o remove() no final, meu tempo de execução demoraria o dobro (Sem Iterator: 13,5 minutos, Com Iterator: 7 minutos).

Como ainda não está perfeito, mas já está melhor e já acrescenta algo à quem procurar o tópico, aí vai meu código:


            //variaveis Globais para calculo dos inadimplentes:
               int enfNum = 0;
               int enfPago = 0;
               int enfNaoPago = 0;
               int tecNum = 0;
               int tecPago = 0;
               int tecNaoPago = 0;
               int auxNum = 0;
               int auxPago = 0;
               int auxNaoPago = 0;
               int totNum = 0;
               int totPago = 0;
               int totNaoPago = 0;

               //inicializa o DAO correspondente
               BoletoDAO dao = new BoletoDAO();
               //armazena na variavel boletos do ano correspondente.
               ArrayList<Boleto> boletos = dao.getInadimplentes(ano);


               //variáveis de controle;
               String chaveAnterior = "";
               boolean enf = false;
               boolean tec = false;
               boolean aux = false;
               int iiii = 0;
               tInicio = System.currentTimeMillis(); 
                for (Iterator<Boleto> it = boletos.iterator(); it.hasNext();) {
                    Boleto bol = it.next();
                    iiii++;
                    String chaveFor = bol.getNuInscProf()+bol.getCdCategoria()+bol.getCdTipo()+bol.getCdTaxa();
                    if(chaveAnterior.compareTo(chaveFor)!=0){
                        
                        //cria variáveis para controlar o tipo de profissional
                        enf = false;
                        tec = false;
                        aux = false;
                        //verifica se a categoria é 01 (enfermeiro)
                        if(bol.getCdCategoria().compareTo("01")==0){
                            enf=true;
                            ++enfNum;
                        }else{
                            //verifica se a categoria é 03 (técnico)
                            if(bol.getCdCategoria().compareTo("03")==0){
                                tec=true;
                                ++tecNum;
                            }else{
                                //verifica se não for 01 ou 03, é 04 (auxiliar)
                                aux=true;
                                ++auxNum;
                            }
                        }
                        
                        ArrayList<Boleto> boletosChave = new ArrayList<>();
                        boletosChave = bol.getChavesIguais(chaveFor,boletos);
                        
                        //var para registrar se tudo está pago
                        boolean pago = true;
                        
                        for (Iterator<Boleto> it2 = boletosChave.iterator(); it2.hasNext();) {
                            Boleto b = it2.next();
                            String cdStatusP    = b.getCdStatusP();
                            if(cdStatusP==null){
                                pago = false;
                            }
                            it2.remove(); //aqui está o PULO DO GATO
                        }
                        
                        if(pago){
                            if(enf) ++enfPago;
                            if(tec) ++tecPago;
                            if(aux) ++auxPago;
                        }else{
                            if(enf) ++enfNaoPago;
                            if(tec) ++tecNaoPago;
                            if(aux) ++auxNaoPago;
                        }
                        
                    }
                    chaveAnterior = chaveFor;
                    it.remove(); //aqui está o PULO DO GATO
                }


               totNum = enfNum + tecNum + auxNum;
               totPago = enfPago + tecPago + auxPago;
               totNaoPago = enfNaoPago + tecNaoPago + auxNaoPago;

Bom pessoal, ainda assim não está bom, está levando 7 minutos para executar o código.
Por enquanto, o melhor código que achei para verificar Strings num array é com lamba (Java8 ):

    public ArrayList<Boleto> getChavesIguais(String chave, List<Boleto> arrayBoletos){
        //cria um novo ArrayList para receber os boletos com a chave correspondente
        ArrayList<Boleto> listaChaves = new ArrayList<>();
        //lambda expression, apenas Java 8+
        arrayBoletos.stream().filter((i) -> (i.getChave().compareTo(chave)==0)).forEach((i) -> {
            listaChaves.add(i);
        });
        //retorna o ArrayList somente c/ boletos iguais à chave.
        return listaChaves;
    }

Por favor, se alguém tiver como dar alguma contribuição… fico ainda mais feliz! :slight_smile:
Obrigado, abs.

[quote=b2000]Pessoal, meu progresso parou por aí.
No final das contas minha conclusão é de que o Java não é tão robusto como falam, pois nada foi dito que resolvesse o problema: consultar Strings (cerca de 46.000) em um ArrayList de objetos (323.000).

Falei com um DBA que me recomendou utilizar materialized view, que nada mais é que: pegar os dados da SQL e jogar em outra tabela, e aí ir atualizando os dados conforme o necessário, no meu caso diariamente…

Vocês tem alguma outra ideia ou concordam que só com a Materialized View meu problema pode ser resolvido?

Abs.[/quote]

O problema não tem nada a ver com Java. Algoritmos de ordem n² ou n³ são lentos em qualquer linguagem. Não tem linguagem/plataforma que resolva algoritmo ruim.

Repito o que já disse acima, isso deve ser feito via SQL. Duas ou três queries dão-te o que queres em muito menos de 7 minutos.

Caros pmlm e rmendes08, obrigado pela contribuição!

Concordo com os dois, também acho que uma SQL bem feita poderia resolver, como acho que se o banco de dados que foi projetado, tivesse sido de fato projetado, minha consulta seria muito mais rápida e eficiente. O algoritmo é lento pois estou tentando trabalhar com muitos dados ao mesmo tempo, mas ainda assim me descontenta saber que em PHP ele roda em 1 minuto (Em Java está levando 7 minutos).

Para explicar o porquê de eu estar trabalhando com tantos dados, vou dizer duas coisas:
1 - O banco em que a SQL roda, é Interbase 6.1 (mais lento que uma tartaruga bêbada) e a SQL não têm nada super “complexo” justamente para não travar o servidor. Atual SQL:

SELECT M.CDCATEGORIA, M.NUINSCPROF, M.CDTIPO, M.CDTAXA, M.NUPARCELA, M.CDSTATUSP FROM MOVBOLETOS M WHERE M.ANO = 2014 AND ( M.CDCATEGORIA like '01' OR M.CDCATEGORIA like '03' OR M.CDCATEGORIA like '04') AND M.CDTIPO like '04' AND M.NUINSCPROF = (SELECT P.NUINSCPROF FROM PROFISSIONAIS P WHERE M.CDCATEGORIA = P.CDCATEGORIA AND M.CDTIPO = P.CDTIPO AND M.CDSUBTIPO = P.CDSUBTIPO AND M.NUINSCPROF = P.NUINSCPROF AND P.CDSITUACAO = 1 ) ORDER BY M.CDCATEGORIA, M.NUINSCPROF, M.CDTIPO, M.CDTAXA, M.NUPARCELA ASC

Essa SQL acima não demora “nada” em termos de MySql rodando em localhost:8080, sempre demorando por volta de 5 segundos. Porém, como ela deve rodar no banco InterBase fazê-la ainda mais complexa seria um tiro no pé visto que nos meus testes, o Banco InterBase em localhost demorou 3 minutos para rodar a SQL acima, e no servidor demorou eternos 20 minutos. Com esse tempo de execução para essa SQL, não consigo imaginar separá-la em duas, ou aumentar sua complexidade…

2 - Os dados que preciso trabalhar envolvem boletos, e eu preciso verificar por chave composta (sim, não há uma chave primária decente nessa tabela) pelos campos “M.CDCATEGORIA, M.NUINSCPROF, M.CDTIPO, M.CDTAXA”. É preciso verificar se, para cada chave, há algum registro não pago e de qual CDCATEGORIA é (01,03 ou 04), correspondendo à tipos de profissionais diferentes.

O desempenho da lógica, antes de implementar os Iterator que falei no post anterior era
11 à 19 minutos para finalizar, após o Iterator, caiu para 5 à 8 minutos. Não tem como não dar crédito ao remove() da Collection Iterator, porém, ainda assim, o PHP deu um banho de performance, não na SQL, mas na hora de executar a lógica, mesmo sem o Iterator, com o uso de for()

Se eu levar em consideração que estou trabalhando com Java Desktop, não consigo entender o porquê da performance ser pior que a do PHP… Para vocês verem que não estou de “sacanagem”, a lógica do código em Java foi passada num dos meus últimos posts da página 2, e agora vou passar a lógica do PHP:

Obs.: a grande diferença dos códigos é que, não fiz tantos tratamentos em JAVA (o código Java ficou bem mais simples para descobrir se foi pago ou não o boleto) e usei arrays bidimensionais e tridimensionais no caso do PHP

Obs2.: veja o código abaixo somente se não acreditar que a mesma lógica possui 700% de performance melhorada em PHP

	//variaveis globais dessa página		
	//ENFERMEIROS:
	$enf_num=0;
	$enf_pago=0;
	$enf_naopago=0;
	//TÉCNICOS:
	$tec_num=0;
	$tec_pago=0;
	$tec_naopago=0;
	//AUXILIAR:
	$aux_num=0;
	$aux_pago=0;
	$aux_naopago=0;
	//TOTAIS
	$tot_num=0;
	$tot_pago=0;
	$tot_naopago=0;

	$sql_boletos="	
		SELECT 	`M`.`NUINSCPROF`, `M`.`CDCATEGORIA`, `M`.`CDTIPO`,
				`M`.`CDTAXA`,`M`.`NUPARCELA`, `M`.`CDSTATUSP`
		FROM `movboletos` as M
		WHERE
			`M`.`ANO` = 2014 AND (
				`M`.`CDCATEGORIA` like '01' OR 
				`M`.`CDCATEGORIA` like '03' OR 
				`M`.`CDCATEGORIA` like '04'
			) 
			AND
			`M`.`CDTIPO` like '04' AND 
			
			`M`.`NUINSCPROF` = (
					SELECT `P`.`NUINSCPROF` FROM `profissionais` as P 
					WHERE 	`M`.`CDCATEGORIA` = `P`.`CDCATEGORIA` AND 
							`M`.`CDTIPO` = `P`.`CDTIPO` AND 
							`M`.`CDSUBTIPO` = `P`.`CDSUBTIPO` AND 
							`M`.`NUINSCPROF` = `P`.`NUINSCPROF` AND 
							`P`.`CDSITUACAO` = 1 )
		ORDER BY
			`M`.`CDCATEGORIA`, `M`.`NUINSCPROF`, `M`.`CDTIPO`, 
			`M`.`CDTAXA`, `M`.`NUPARCELA` ASC
		";		
		//executa a consulta no banco		
		$qry_boletos= mysql_query($sql_boletos);		
		//variaveis de controle
		$taxas = array();
		$boletos = array(); 
		$atual=0; //salva as infos do último profissional verificado
		//varre os dados gerados pela SQL		
		while($linha = mysql_fetch_array($qry_boletos)){
			
			//define uma variavel como "chave" para separar em conjuntos cada uma das taxas. É como se fosse o "código do boleto"
			$chave = $linha['NUINSCPROF'].$linha['CDCATEGORIA'].$linha['CDTIPO'].$linha['CDTAXA'];
			
			//se a chave atual for diferente da anterior, é outro boleto, portanto mais uma anuidade/multa foi acrescentada
			if($atual != $chave ){
				//verifica qual variavel deve receber valor de acordo com a categoria do boleto
				if($linha['CDCATEGORIA']==1){	++$enf_num;		}
				if($linha['CDCATEGORIA']==3){	++$tec_num;		}
				if($linha['CDCATEGORIA']==4){	++$aux_num;		}
				//array recebe um novo indice que corresponde a uma chave, ou, à um novo boleto/taxa/anuidade/multa gerada				
				$taxas[]=$chave;
				
			}
			
			$boletos[$chave][] = array(
				"NUINSCPROF"=>$linha['NUINSCPROF'],		//CHAVES:	numero da inscrição 
				"CDCATEGORIA"=>$linha['CDCATEGORIA'],	//			código da categoria
				"CDTIPO"=>$linha['CDTIPO'],				//			código do tipo de profissional	
				"CDTAXA"=>$linha['CDTAXA'],			//BOLETO:		código da taxa - 0 (anuidade), 7 e 8 (multas)
				"NUPARCELA"=>$linha['NUPARCELA'],	//				numero da parcela (0 é única - podendo ser 1,2,3..)
				"CDSTATUSP"=>$linha['CDSTATUSP'],	//				cod Status pagamento (!=NULL > PAGO, Resto > NÃO PAGO)
			);

			//seta esse valor para variável, para verificar no próximo registro se ainda estamos tratando do mesmo boleto
			$atual = $linha['NUINSCPROF'].$linha['CDCATEGORIA'].$linha['CDTIPO'].$linha['CDTAXA'];
		}
		
		//varre de taxa em taxa
		for($i=0;$i<sizeof($taxas);$i++){	
			//variável $chave armazena o "código do boleto", formada pelo núm inscrição + cód. categoria + cód. taxa
			$chave	=	$taxas[$i];
			
			//varre os registros do array movboletos no ano definido no for anterior
			for($x=0;$x<sizeof($boletos[$chave]);$x++){				

				//se anuidade (taxa=0), parcela cheia (numparcela=0) e foi paga (CDSTATUSP!=NULL), diga que já está pago
					if( $boletos[$chave][$x]['CDTAXA']==0 && $boletos[$chave][$x]['NUPARCELA']==0 && $boletos[$chave][$x]['CDSTATUSP']!=NULL){
						//soma como mais um boleto pago 
						//verifica qual variavel deve receber valor de acordo com a categoria do boleto
						//varre os lancamentos dessa taxa para verificar se não há outra parcela não paga				
						$todasParcelasPagas=true;
						for($z=0;$z<sizeof($boletos[$chave]);$z++){
							//se cdstatusp==NULL (não paga)
							if($boletos[$chave][$z]['CDSTATUSP']==NULL){
								//seta a var $todasParcelasPagas como false pois nem todas parcelas foram pagas
								$todasParcelasPagas=false;
							}
						}
						
						//se $todasParcelasPagas=true então soma como mais uma taxa paga
						if($todasParcelasPagas){
						
							//soma mais uma PAGA
							if($boletos[$chave][$x]['CDCATEGORIA']==1){	++$enf_pago; }
							if($boletos[$chave][$x]['CDCATEGORIA']==3){	++$tec_pago; }
							if($boletos[$chave][$x]['CDCATEGORIA']==4){	++$aux_pago; }
						
						}else{
						
							//soma mais uma NÃO paga 
							if($boletos[$chave][$x]['CDCATEGORIA']==1){	++$enf_naopago; }
							if($boletos[$chave][$x]['CDCATEGORIA']==3){	++$tec_naopago; }
							if($boletos[$chave][$x]['CDCATEGORIA']==4){	++$aux_naopago; }
							
						}
						//sai desse for, indo para próxima taxa
						break;
					}
					
				//esse if foi feito para encontrar o caso raro que ocorre quando há somente uma parcela única (cdtaxa=0), não paga e nenhuma outra parcela, nesse caso, apenas verificamos se é isso mesmo e adicionamos como mais um registro não pago
					if( $boletos[$chave][$x]['CDTAXA']==0 && $boletos[$chave][$x]['NUPARCELA']==0 && $boletos[$chave][$x]['CDSTATUSP']==NULL){
							//se o núm. de registros dessa chave for < 2, quer dizer que só há um único registro,portanto, some
							if(sizeof($boletos[$chave])<2){
								//soma mais um boleto não pago
								if($boletos[$chave][$x]['CDCATEGORIA']==1){	++$enf_naopago; }
								if($boletos[$chave][$x]['CDCATEGORIA']==3){	++$tec_naopago; }
								if($boletos[$chave][$x]['CDCATEGORIA']==4){	++$aux_naopago; }
								
							}
							
					}
						
				//se anuidade (taxa=0) e parcela normal (numparcela!=0)
					if( $boletos[$chave][$x]['CDTAXA']==0 && $boletos[$chave][$x]['NUPARCELA']!=0){						
							//var de controle
							$todasParcelasPagas=true;							
							//for que varre o array desta anuidade p/ verificar se todas parcelas foram pagas
							for($z=0;$z<sizeof($boletos[$chave]);$z++){
								//se nuparcela!=0 (parcela normal) e cdstatusp==NULL (não paga)
								if($boletos[$chave][$z]['NUPARCELA']!=0 && $boletos[$chave][$z]['CDSTATUSP']==NULL){
									//seta a var $todasParcelasPagas como false pois nem todas parcelas foram pagas
									$todasParcelasPagas=false;
								}
							}
							//se $todasParcelasPagas=true então soma como mais uma taxa paxa
							if($todasParcelasPagas){

								//soma mais uma PAGA
								if($boletos[$chave][$x]['CDCATEGORIA']==1){	++$enf_pago; }
								if($boletos[$chave][$x]['CDCATEGORIA']==3){	++$tec_pago; }
								if($boletos[$chave][$x]['CDCATEGORIA']==4){	++$aux_pago; }

							}else{

								//soma mais uma NÃO paga 
								if($boletos[$chave][$x]['CDCATEGORIA']==1){	++$enf_naopago; }
								if($boletos[$chave][$x]['CDCATEGORIA']==3){	++$tec_naopago; }
								if($boletos[$chave][$x]['CDCATEGORIA']==4){	++$aux_naopago; }
								
							}
							//sai desse for, indo para próxima taxa
							break;
					}
						
				//qualquer taxa em que cdtaxa!=0 não é preciso nenhuma outra verificação, só se verifica se o == e != NULL
					if( $boletos[$chave][$x]['CDTAXA']!=0) {
						if($boletos[$chave][$x]['CDSTATUSP']!=NULL){
							//soma mais uma PAGA 
							if($boletos[$chave][$x]['CDCATEGORIA']==1){	++$enf_pago; }
							if($boletos[$chave][$x]['CDCATEGORIA']==3){	++$tec_pago; }
							if($boletos[$chave][$x]['CDCATEGORIA']==4){	++$aux_pago; }
						}
						if($boletos[$chave][$x]['CDSTATUSP']==NULL){
							//soma mais uma NÃO paga 
							if($boletos[$chave][$x]['CDCATEGORIA']==1){	++$enf_naopago; }
							if($boletos[$chave][$x]['CDCATEGORIA']==3){	++$tec_naopago; }
							if($boletos[$chave][$x]['CDCATEGORIA']==4){	++$aux_naopago; }
						}
						//sai desse for, indo para próxima taxa
						break;
					}
			}//fim 2º FOR						
		}//fim 1º FOR


		//efetua o cálculo do total
		$tot_num=$enf_num+$tec_num+$aux_num;
		$tot_pago=$enf_pago+$tec_pago+$aux_pago;
		$tot_naopago=$enf_naopago+$tec_naopago+$aux_naopago;		
		

Aparentemetne no PHP usas algo que no Java pode ser o equivalente e um Map.

Acredito que isto:

 $boletos[$chave][] = array(  
            "NUINSCPROF"=>$linha['NUINSCPROF'],      //CHAVES:   numero da inscrição   
            "CDCATEGORIA"=>$linha['CDCATEGORIA'],    //          código da categoria  
            "CDTIPO"=>$linha['CDTIPO'],              //          código do tipo de profissional    
            "CDTAXA"=>$linha['CDTAXA'],          //BOLETO:       código da taxa - 0 (anuidade), 7 e 8 (multas)  
            "NUPARCELA"=>$linha['NUPARCELA'],    //              numero da parcela (0 é única - podendo ser 1,2,3..)  
            "CDSTATUSP"=>$linha['CDSTATUSP'],    //              cod Status pagamento (!=NULL > PAGO, Resto > NÃO PAGO)  
        );  

seja “traduzido” para isto:

Map map<String, List<Boleto>> boletosChave = new HashMap<String, List<Boleto>>();

...
while(...
...
if (boletosChave.get(chave)==null){
     boletosChave.put(chave, new ArrayList<Boleto>());
}
boletosChave.get(chave).add(boleto);

O teu código php será mais ou menos assim em java: podes verificar a performance deste contra o teu de php?

Ainda há uma grande diferença, que é no php ele faz a lógica a ler da BD e aqui já leu tudo para um ArrayList. Por isso, ainda pode ser melhorado, passando esta lógica para o dao.

  //variaveis globais dessa página
        //ENFERMEIROS:
        int enfNum = 0;
        int enfPago = 0;
        int enfNaoPago = 0;
        //TÉCNICOS:
        int tecNum = 0;
        int tecPago = 0;
        int tecNaoPago = 0;
        //AUXILIAR:
        int auxNum = 0;
        int auxPago = 0;
        int auxNaoPago = 0;
        //TOTAIS
        int totNum = 0;
        int totPago = 0;
        int totNaoPago = 0;

        List<Boleto> lista = dao.getInadimplentes(ano);
//variaveis de controle
        Map<String, List<Boleto>> boletos = new HashMap<String, List<Boleto>>();
        String atual = ""; //salva as infos do último profissional verificado
//varre os dados gerados
        for (Boleto linha : lista) {

            //define uma variavel como "chave" para separar em conjuntos cada uma das taxas. É como se fosse o "código do boleto"
            String chave = linha.getNuInscProf() + linha.getCdCategoria() + linha.getCdTipo() + linha.getCdTaxa();


            //se a chave atual for diferente da anterior, é outro boleto, portanto mais uma anuidade/multa foi acrescentada
            if (!atual.equals(chave) {
                //verifica qual variavel deve receber valor de acordo com a categoria do boleto
                if (linha.getCdCategoria().equals("01")) {
                    enfNum++;
                }
                if (linha.getCdCategoria().equals("03")) {
                    tecNum++;
                }
                if (linha.getCdCategoria().equals("04")) {
                    auxNum++;
                }

            }

            if (boletos.get(chave) == null) {
                boletos.put(chave, new ArrayList<Boleto>());
            }
            boletos.get(chave).add(linha);

            //seta esse valor para variável, para verificar no próximo registro se ainda estamos tratando do mesmo boleto
            atual = chave;
        }

        //varre de taxa em taxa
        for (String chave : boletos.keySet()) {
            //variável $chave armazena o "código do boleto", formada pelo núm inscrição + cód. categoria + cód. taxa

            boolean todasParcelasPagas = true;
            boolean first = true;
            boolean enf = false;
            boolean tec = false;
            boolean aux = false;
            //varre os registros do array movboletos no ano definido no for anterior
            for (Boleto b : boletos.get(chave)) {

                if (first) {
                    if (b.getCdCategoria().equals("01")) {
                        enf = true;
                    }
                    if (b.getCdCategoria().equals("03")) {
                        tec = true;
                    }
                    if (b.getCdCategoria().equals("04")) {
                        aux = true;
                    }
                    first = false;
                }
                if (b.getCdStatusP() == null) {
                    todasParcelasPagas = false;
                    break;
                }
            }
            first = true;
            if (todasParcelasPagas) {
                if (enf) {
                    ++enfPago;
                }
                if (tec) {
                    ++tecPago;
                }
                if (aux) {
                    ++auxPago;
                }
            } else {
                if (enf) {
                    ++enfNaoPago;
                }
                if (tec) {
                    ++tecNaoPago;
                }
                if (aux) {
                    ++auxNaoPago;
                }
            }
        }
        totNum = enfNum + tecNum + auxNum;
        totPago = enfPago + tecPago + auxPago;
        totNaoPago = enfNaoPago + tecNaoPago + auxNaoPago;

Vou testar e já posto… Abraços.

[size=18]És o cara pmlm ![/size]

A performance da manipulação dos dados foi, simplesmente INCRÍVEL!
Desse um banho agora, parabéns!

Tempo de execução p/ Tratar os Dados ANTES:

Localhost MySql: 7:05 minutos
Localhost InterBase: 6:45 minutos

Tempo de execução p/ Tratar os Dados COM SEU CÓDIGO:

Localhost MySql: 15 segundos
Localhost InterBase: 1:40 minutos

Um ganho de performance de 600% (Interbase) à 2600% (MySql) - perfeito!
Agora, com aproximadamente 3 min de tempo para executar o código, posso exibir antes uma msg avisando que o procedimento leva alguns minutos (mas sei que não passa de 3).

Mas não é simplesmente “obrigado, copiei, colei e tchau”, verifiquei que:

[size=24]1 ->[/size] ao invés de varrer boletos dentro de boletos para encontrar os boletos de uma determinada chave, você criou um Map<String, List> que (via for) recebia novas keys na String à cada novo “código de boleto” e armazenava o Array de boletos encontrados - trecho abaixo: (E você pode me afirmar se o array boletos entende que boletos.get(chave) == null é a hora de “trocar de chave” porque, como ele não achou esse valor no boletos, então deve adicionar um novo arrayList com essa chave, e depois dá o boletos.get(chave).add(linha); apenas para adicionar este novo boleto à lista? É isso?)

for (Boleto linha : lista) {  
                    //define uma variavel como "chave"
                    String chave = linha.getNuInscProf() + linha.getCdCategoria() + linha.getCdTipo() + linha.getCdTaxa();  
                    if (boletos.get(chave) == null) {  
                        boletos.put(chave, new ArrayList<>());  
                    }  
                    boletos.get(chave).add(linha);  
                    //atual = chave, para verificar no próximo registro se ainda estamos tratando do mesmo boleto  
                    atual = chave;  
                }  

[size=24]2 ->[/size] Como já tinhas separado por chaves, ficou mais simples o for de “lógica” com o for (String chave : boletos.keySet()) { seguido depois pelo for (Boleto b : boletos.get(chave)) { que então varria cada “código de boleto” e fazia o resto como já estava fazendo…

[size=24]3 ->[/size] Você diz que [quote=pmlm]Ainda há uma grande diferença, que é no php ele faz a lógica a ler da BD e aqui já leu tudo para um ArrayList. Por isso, ainda pode ser melhorado, passando esta lógica para o dao.[/quote] o DAO pode ser alterado para ser ainda melhor? O que você quer dizer com isso?

Ficou tão simples, e muito mais avançado, eu realmente não conseguiria pensar no KeySet nem nessa verificação if (boletos.get(chave) == null) { cara, você ajudou MUITO! \o/

Depois dessa, só tenho a agradecer pela ajuda de TODOS !

Código final, com performance MELHOR que a do PHP! :slight_smile: (Ufa, agora voltei a botar fé no Java rsrsrs)

            //variável que será usada para retornar os dados.
            Map<String, String> dados = new HashMap<>();
               //inicializa o DAO correspondente
               SBoletoDAO dao = new SBoletoDAO();
               //armazena na variavel boletos do ano correspondente.
               List<Boleto> lista = dao.getInadimplentes(ano);

               long tExecucao = System.currentTimeMillis() - tInicio; 
               System.out.println("Tempo para executar o listaInadimplentes: "+tExecucao+" ms");

                //variaveis de controle  
                //MAP que irá armazenar os dados dos boletos
                Map<String, List<Boleto>> boletos = new HashMap<>();  
                //salva as infos do último profissional verificado
                String atual = "";   
                
                //varre os dados gerados  
                for (Boleto linha : lista) {  

                    //define uma variavel como "chave" para separar em conjuntos cada uma das taxas. É como se fosse o "código do boleto"  
                    String chave = linha.getNuInscProf() + linha.getCdCategoria() + linha.getCdTipo() + linha.getCdTaxa();  

                    if (boletos.get(chave) == null) {  
                        boletos.put(chave, new ArrayList<>());  
                    }  
                    boletos.get(chave).add(linha);  

                    //seta esse valor para variável, para verificar no próximo registro se ainda estamos tratando do mesmo boleto  
                    atual = chave;  
                }  
                //varre de taxa em taxa  
                for (String chave : boletos.keySet()) {  
                    //variável $chave armazena o "código do boleto", formada pelo núm inscrição + cód. categoria + cód. taxa  

                    boolean todasParcelasPagas = true;  
                    boolean first = true;  
                    boolean enf = false;  
                    boolean tec = false;  
                    boolean aux = false;  
                    //varre os registros do array movboletos no ano definido no for anterior  
                    for (Boleto b : boletos.get(chave)) {  

                        if (first) {  
                            if (b.getCdCategoria().equals("01")) {  
                                enf = true;
                                enfNum++;
                            }
                            if (b.getCdCategoria().equals("03")) {  
                                tec = true;
                                tecNum++;
                            }
                            if (b.getCdCategoria().equals("04")) {  
                                aux = true;
                                auxNum++;
                            }
                            first = false;  
                        }  
                        if (b.getCdStatusP() == null) {  
                            todasParcelasPagas = false;  
                            break;  
                        }  
                    }  
                    first = true;  
                    if (todasParcelasPagas) {  
                        if (enf) {  
                            ++enfPago;  
                        }  
                        if (tec) {  
                            ++tecPago;  
                        }  
                        if (aux) {  
                            ++auxPago;  
                        }  
                    } else {  
                        if (enf) {  
                            ++enfNaoPago;  
                        }  
                        if (tec) {  
                            ++tecNaoPago;  
                        }  
                        if (aux) {  
                            ++auxNaoPago;  
                        }  
                    }  
                }

               tExecucao = System.currentTimeMillis() - tInicio; 
               System.out.println("Tempo para executar o FOR: "+tExecucao+" ms");


               tInicio = System.currentTimeMillis(); 


               totNum = enfNum + tecNum + auxNum;
               totPago = enfPago + tecPago + auxPago;
               totNaoPago = enfNaoPago + tecNaoPago + auxNaoPago;
               
                DecimalFormat df = new DecimalFormat("#,##0.##");
        
                float fenfnum      = (float) enfNum;
                float ftecnum      = (float) tecNum;
                float fauxnum      = (float) auxNum;
                float ftotnum      = (float) totNum;

                float enfpago     = (float) enfPago;
                float enfnaopago  = (float) enfNaoPago;
                float tecpago     = (float) tecPago;
                float tecnaopago  = (float) tecNaoPago;
                float auxpago     = (float) auxPago;
                float auxnaopago  = (float) auxNaoPago;
                float totpago     = (float) totPago;
                float totnaopago  = (float) totNaoPago;

                //totais
                dados.put("totnum" ,        ""+df.format(totNum));
                dados.put("totpago" ,       ""+df.format(totPago));
                dados.put("totnaopago" ,    ""+df.format(totNaoPago));
                //emitidos por categoria
                dados.put("enfnum" ,        ""+df.format(enfNum));
                dados.put("tecnum" ,        ""+df.format(tecNum));
                dados.put("auxnum" ,        ""+df.format(auxNum));
                //pagos:
                dados.put("enfpago" ,       ""+df.format(enfPago));
                dados.put("tecpago" ,       ""+df.format(tecPago));
                dados.put("auxpago" ,       ""+df.format(auxPago));
                //nao pagos:
                dados.put("enfnaopago" ,    ""+df.format(enfNaoPago));
                dados.put("tecnaopago" ,    ""+df.format(tecNaoPago));
                dados.put("auxnaopago" ,    ""+df.format(auxNaoPago));

Mais uma vez o GUJ deu um banho em quaisquer outros fóruns de Java, claro, pela ajuda de vocês! Hoje podia ser sexta-feira hehehe
Um abraço a todos e até logo! :slight_smile:

O facto de dizeres que PHP era muito mais rápido que Java não fazia sentido a não ser que a lógica usada fosse diferente. E ao ver o teu códido de PHP confirmei que sim, era.

o if não é para trocar de chave, é para ver se a chave já existe no mapa. Isso funciona mesmo que os boletos não venham ordenados pela chave. Uma mapa funciona como um indice para os objetos. Eu só verifico se já existe uma lista naquele indice, para o add não dar NullPointerException se não existisse a lista.
Ao ver agora o código o guardar a chave atual é desnecessario - e não estás a usar isso para nada.

[quote]
Como já tinhas separado por chaves, ficou mais simples o for de “lógica” com o for (String chave : boletos.keySet()) { seguido depois pelo for (Boleto b : boletos.get(chave)) { que então varria cada “código de boleto” e fazia o resto como já estava fazendo… [/quote]
Isso era a lógica que já estava no php. Simplesmente fiz igual em java.

No teu código php estás a ler linha a linha da bd e vais logo comparando chaves e adicionando a estas estruturas. Aqui estás a ler para uma lista e depois ler essa lista linha a linha.

No teu DAO deves ter algo como while (rs.next()){...
Se colocares aí o que tens em for (Boleto linha : lista) { pode ser que tenhas ainda alguma melhoria. Mas isso não sei precisar.

cmlm

Acho que já tenho o que você está falando em meu DAO. Veja o código do DAO abaixo:

public List<Boleto> getInadimplentes(int ano){

        //variável que será usada para retornar os dados.
        List<Boleto> boletos = new ArrayList<>();        

            String sql = "SELECT M.CDCATEGORIA, M.NUINSCPROF, M.CDTIPO, M.CDTAXA, M.NUPARCELA, M.CDSTATUSP FROM MOVBOLETOS M "
                    + "WHERE "
                        + "M.ANO = "+ano+" AND ( "
                            + "M.CDCATEGORIA like '01' OR M.CDCATEGORIA like '03' OR M.CDCATEGORIA like '04'"
                            + ") AND "
                        + "M.CDTIPO like '04' AND "
                        + "M.NUINSCPROF = ("
                            + "SELECT P.NUINSCPROF FROM PROFISSIONAIS P "
                            + "WHERE "
                                + "M.CDCATEGORIA = P.CDCATEGORIA AND "
                                + "M.CDTIPO = P.CDTIPO AND "
                                + "M.CDSUBTIPO = P.CDSUBTIPO AND "
                                + "M.NUINSCPROF = P.NUINSCPROF AND "
                                + "P.CDSITUACAO = 1 "
                            + ") "
                    + "ORDER BY "
                        + "M.CDCATEGORIA, M.NUINSCPROF, M.CDTIPO, M.CDTAXA, M.NUPARCELA ASC"
                    //+ " LIMIT 0,30";
                    + "";

            ResultSet rs;

            try {
                //prepara a SQL para ser executada
                PreparedStatement ps = this.connection.prepareStatement(sql);
                //executa a query
                rs = ps.executeQuery();
                //cria uma List que ira armazenar os profissionais
                //enquanto houver registros, faca:
                while(rs.next()){

                    Boleto boleto = new Boleto();
                    
                    boleto.setChave(rs.getString("NUINSCPROF")+rs.getString("CDCATEGORIA")+rs.getString("CDTIPO")+rs.getInt("CDTAXA"));
                    boleto.setNuInscProf(rs.getString("NUINSCPROF"));
                    boleto.setCdCategoria(rs.getString("CDCATEGORIA"));
                    boleto.setCdTipo(rs.getString("CDTIPO"));
                    boleto.setCdTaxa(rs.getInt("CDTAXA"));
                    boleto.setNuParcela(rs.getInt("NUPARCELA"));
                    boleto.setCdStatusP(rs.getString("CDSTATUSP"));

                    //adicione este boleto encontrado na lista de boletos
                    boletos.add(boleto);
                }

                //finaliza o PreparedStatement
                ps.close();
                //finaliza o ResultSet
                rs.close();
                

            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }

        }        
        //retorna o ArrayList
        return boletos;        
    }

Você já não poderia montar seu Map aí mesmo comparando a lista? Não sei, talvez dê no mesmo, mas…

Exata, era isso que eu me referia. Em vez de devolver uma List, o teu dao já devolvia o Map.

Declaras boletos como Map e em vez de //adicione este boleto encontrado na lista de boletos boletos.add(boleto);

fazes logo

   String chave = boleto.getNuInscProf() + boleto.getCdCategoria() + boleto.getCdTipo() + boleto.getCdTaxa();    
  
   if (boletos.get(chave) == null) {    
         boletos.put(chave, new ArrayList&lt;&gt;());    
   }    
   boletos.get(chave).add(boleto);    

Com isto podes retirar o for que constroi o map na outra classe

Obrigado pmlm e adriano_si!

Agora entendi! hehehe

Isso é uma boa prática? Pensei em deixar isolado nos DAO apenas as partes de consulta ao banco de dados, retornando para a classe controller “Inadimplentes”, apenas o que veio pela SQL, e então lá, fazer o tratamento das informações e mandar certinho pra View (JDialog) “Inadimplentes”…

Mas enfim… Vou implementar, passar o código e o resultado dos testes!
Abs.