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

42 respostas
B

E aí pessoal, tudo bem? Tô ficando doido e não aguento mais ficar doido sozinho, por isso vim recorrer a vocês… :smiley:

Tenho um código em um JFrame que calcula o número de taxas emitidas, as pagas e não pagas por categoria (Enfermeiro, Técnico e Auxiliar). As duas tabelas consultadas são profisisonais (92.000 registros) e movboletos (2.100.000).

Para armazenar esta listagem, utilizei ArrayList, Para conexão com o banco MySQL usei JDBC com o drive mysql-connector-java-5.1.34-bin.jar

O problema é que para executar a query está demorando 10 segundos porém para retornar o resultado “na tela”, aproximadamente 25 minutos (impossível esperar que o usuário aguente tamanha espera).
Como não consigo imaginar a SQL de outra forma, nem conheço à fundo a linguagem em Java (é meu primeiro programa após 5 anos de PHP estruturado), recorro a vocês para entender e resolver o porquê de tanta demora. O problema é com o ArrayList, minha programação ou a SQL?
Segue abaixo as classes utilizadas:

1 - Classe que gera o ArrayList com os boletos:

public class BoletoDAO {
    
    private Connection connection;

    public BoletoDAO(){
        this.connection = new ConnectionFactory().getConnection();
    }
    
        public ArrayList<Boleto> listaInadimplentes(int ano){
    
           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";
            
            try{
			
		//prepara a SQL para ser executada
		PreparedStatement ps = this.connection.prepareStatement(sql);
		
		//executa a query
		ResultSet rs = ps.executeQuery();
		
		//cria uma List que ira armazenar os profissionais
		ArrayList<Boleto> boletos = new ArrayList<Boleto>();
		
                
		//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();
		
		//retorna a LIST para quem chamar a funcao "inadimplentes"
		return boletos;
		
		}catch(SQLException e){
			throw new RuntimeException(e);
		}
    
        }
    
    
}

O código acima demora 10 segundos para rodar no código JAVA em localhost, sendo que via phpmyadmin o tempo de execução da SQL é de 6 segundos.

2 - método initComponents da JFrame que exibe na tela o número de registros:

public void initComponentsTrue(){
..
..
..
        
        //abaixo fazemos a lógica dos inadimplentes:
        
        
        //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 de 2014.
    ArrayList<Boleto> boletos = dao.listaInadimplentes(2014);
    
    String chaveAnterior = "";
    
    for(Boleto bol : boletos){            
       
                String chaveFor = bol.getNuInscProf()+"-"+bol.getCdCategoria()+"-"+bol.getCdTipo()+"-"+bol.getCdTaxa();
                
                //se a chaveAnterior for diferente da chaveFor, é uma nova chave, portanto:
                if(!chaveAnterior.equals(chaveFor)){
                    
                    //cria variáveis para controlar o tipo de profissional
                    boolean enf = false;
                    boolean tec = false;
                    boolean aux = false;
                    //verifica se a categoria é 01 (enfermeiro)
                    if(bol.getCdCategoria().equals("01")){
                        enf=true;
                        ++enfNum;
                    }
                    //verifica se a categoria é 03 (técnico)
                    if(bol.getCdCategoria().equals("03")){
                        tec=true;
                        ++tecNum;
                    }
                    //verifica se a categoria é 04 (auxiliar)
                    if(bol.getCdCategoria().equals("04")){
                        aux=true;
                        ++auxNum;
                    }
                    
                    ArrayList<Boleto> boletosChave = new ArrayList<>();
                    boletosChave = bol.getChavesIguais(chaveFor,boletos);
                    
                    for(Boleto b : boletosChave){
                    
                        //variaveis de registro para os if's
                        int cdTaxa          = b.getCdTaxa();
                        int nuParcela       = b.getNuParcela();
                        String cdStatusP    = b.getCdStatusP();
                        
                        if(cdTaxa == 0 && nuParcela == 0 && cdStatusP != null){
                            //cria variável para controlar se as parcelas foram pagas
                            boolean todasParcelasPagas = true;
                            //cria novo array que será varrido para ver se há alguma parcela não paga dessa chave
                            ArrayList<Boleto> varreBoletos = new ArrayList<>();
                            varreBoletos = b.getChavesIguais(chaveFor,boletos);
                            //para cada boleto dentro desse ArrayList
                            for(Boleto outrosBoletos : varreBoletos){
                                //verifica se algum tem o CDSTATUSP == NULL, pois aí não foram todos pagos
                                if(outrosBoletos.getCdStatusP() == null){
                                    todasParcelasPagas = false;
                                }
                            }
                            //se todas parcelas foram pagas, soma como mais uma taxa paga, se não, como não paga
                            if(todasParcelasPagas){
                                if(enf) ++enfPago;
                                if(tec) ++tecPago;
                                if(aux) ++auxPago;                                
                            }else{
                                if(enf) ++enfNaoPago;
                                if(tec) ++tecNaoPago;
                                if(aux) ++auxNaoPago;
                            }

                            //sai desse for, passando pra próxima chave
                            break;
                        }

                        //se há somente uma parcela única (cdtaxa=0), não paga e nenhuma outra parcela adicionamos como mais um registro não pago
                        if(cdTaxa==0 && nuParcela==0 && cdStatusP==null){
                            //se o núm. de registros dessa chave for < 2, 
                            //quer dizer que só há um único registro,portanto, some mais um não pago
                            if(boletosChave.size()<2){
                                if(enf) ++enfNaoPago;
                                if(tec) ++tecNaoPago;
                                if(aux) ++auxNaoPago;                                        
                                //sai desse for, passando pra próxima chave
                                break;
                            }
                        }
                        
                        //se anuidade (taxa=0) e parcela normal (numparcela!=0)
                        if(cdTaxa == 0 && nuParcela != 0){
                                    
                            //cria variável para controlar se as parcelas foram pagas
                            boolean todasParcelasPagas = true;

                            //cria novo array que será varrido para ver se há alguma parcela não paga dessa chave
                            ArrayList<Boleto> varreBoletos = new ArrayList<>();
                            varreBoletos = b.getChavesIguais(chaveFor,boletos);

                            //para cada boleto dentro desse ArrayList
                            for(Boleto outrosBoletos : varreBoletos){
                                //se nuparcela!=0 (parcela normal) e cdstatusp==NULL (não paga)
                                if(outrosBoletos.getNuParcela() != 0 && outrosBoletos.getCdStatusP() == null){
                                    todasParcelasPagas = false;
                                }
                            }

                           //se todas parcelas foram pagas, soma como mais uma taxa paga, se não, como não paga
                            if(todasParcelasPagas){
                                if(enf) ++enfPago;
                                if(tec) ++tecPago;
                                if(aux) ++auxPago;                                
                            }else{
                                if(enf) ++enfNaoPago;
                                if(tec) ++tecNaoPago;
                                if(aux) ++auxNaoPago;
                            }

                            //sai desse for, passando pra próxima chave
                            break;
                        }
                        //qualquer taxa em que cdtaxa!=0 não é preciso nenhuma outra verificação, 
                        //só se verifica se o StatusP é == OU != de NULL
                        if(cdTaxa != 0){
                                    
                            //se cdStatusP == NULL, esta taxa não foi paga
                            if(cdStatusP==null){
                                if(enf) ++enfNaoPago;
                                if(tec) ++tecNaoPago;
                                if(aux) ++auxNaoPago;
                            }else{
                                if(enf) ++enfPago;
                                if(tec) ++tecPago;
                                if(aux) ++auxPago;   
                            }

                            //sai desse for, passando pra próxima chave
                            break;
                        }
                        
                    }//fim do for(Boleto b : boletosChave){
                    
                }//fim do if(!chaveAnterior.equals(chaveFor)){
                
                //ao final, seta que a chaveAnterior vira a chaveFor, para comparação com o próximo registro
                chaveAnterior = chaveFor;
        
    }
    
    totNum = enfNum + tecNum + auxNum;
    totPago = enfPago + tecPago + auxPago;
    totNaoPago = enfNaoPago + tecNaoPago + auxNaoPago;
    
    //fim da lógica para cálculo dos Inadimplentes.
   

        jTable1.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {"Enfermeiro", enfNum, enfPago, enfNaoPago},
                {"Técnico", tecNum, tecPago, tecNaoPago},
                {"Auxiliar", auxNum, auxPago, auxNaoPago},
                {"Total", totNum, totPago, totNaoPago}
            },
            new String [] {
                "Categoria", "Emitidas", "Pagas", "Não Pagas"
            }
        ) 
...
...
...
    }

Então… eu não sei mais o que fazer, será que utilizar matriz ao invés do ArrayList é melhor? O que devo fazer? Me ajudem hehehe

Um abraço! Obrigado![/b]

42 Respostas

B

O código, na sua máquina, leva aproximadamente 10 segundos pra executar e remotamente, demora aquela eternidade, é isso?
ps: No sql, eu trocaria o like pelo = mesmo.

M.CDCATEGORIA  = '01' OR M.CDCATEGORIA = '03' OR M.CDCATEGORIA = '04'

Aparentemente, o SQL em si está correto. Já chegou a executar o SQL direto no banco de dados? Quanto tempo levou?

S

b2000:

Então… eu não sei mais o que fazer, será que utilizar matriz ao invés do ArrayList é melhor? O que devo fazer? Me ajudem hehehe

Um abraço! Obrigado!

Você ja pensou em utilizar paginação??? Quem sabe pode lhe ajudar…

Esse link foi de um usuário que estava dando estouro na HEAP por exibir muitos itens na resposta, o que parece ser seu caso.
Java heap space no Hibernate

Abraços

B

bomba544:
O código, na sua máquina, leva aproximadamente 10 segundos pra executar e remotamente, demora aquela eternidade, é isso?
ps: No sql, eu trocaria o like pelo = mesmo.

M.CDCATEGORIA  = '01' OR M.CDCATEGORIA = '03' OR M.CDCATEGORIA = '04'

Aparentemente, o SQL em si está correto. Já chegou a executar o SQL direto no banco de dados? Quanto tempo levou?

Não… os testes estão sendo feitos em localhost, a SQL talvez demore “mais do que eu imaginava”, mas é a melhor performance se comparada à fazer 2 SQLs e salvá-las de um jeito ordenado no ArrayList.
Já o seu esquema do = ao invés de like, testei a performance e nesse caso, o like ficou com média de 3.50 segs enquanto que o = ficou com 3.60. Nenhuma diferença praticamente… ;/

schiefler:

Você ja pensou em utilizar paginação??? Quem sabe pode lhe ajudar…

Esse link foi de um usuário que estava dando estouro na HEAP por exibir muitos itens na resposta, o que parece ser seu caso.
Java heap space no Hibernate

Abraços

Bom, a páginação não vejo como “mal necessário” pois a SQL em si, como mostrei no caso acima, leva menos de 4 segundos para rodar no banco, o problema é a forma de verificar os dados trazidos.
Vi no link que passasse a utilização de serialização com java.io.ObjectInputStream & ObjectOutputStream, mas será que nesse caso é necessário?

O maior problema não tem sido a SQL mas sim o tratamento dos dados.
Verifiquei que minha forma de verificar num ArrayList se uma chave existe está demorando ANOS para ser executada, talvez isso seja o maior problema, veja o trecho do código que faz isso:

Função que é chamada para trabalhar com cada chave dentro do ArrayList de boletos (323.000 linhas de dados, aproximadamente umas 46.000 chaves):

public ArrayList<Boleto> getChavesIguais(String chave, ArrayList<Boleto> array){
                
        ArrayList<Boleto> listaChaves = new ArrayList<>();
        
        for(Boleto b : array){
            if(b.getChave().equals(chave)){
                listaChaves.add(b);
            }
        }
        
        return listaChaves;
    }

Vi que a cada iteração dessa, demoramos 15 milissegundos, para 323.000 registros dá uma soma terrível.
Por isso, como faço para armazenar com a melhor performance em um ArrayList/outra Collection/ de acordo com uma chave (como pode ser visto no código acima)?

Nesse caso meu problema é que tenho vários boletos e preciso trabalhar com todas as linhas e separá-los em chaves para fazer cálculos p/ descobrir se É MAIS UM BOLETO E ( FOI PAGO ou NÃO FOI PAGO ).
Vou exibir algumas linhas de dados para verem o problema (ver printscreen anexado).

Agradeço MUITO a atenção de vocês! Abraços.

B

Porque não tratar se foi pago ou não, diretamente na consulta? Algo do tipo:

SELECT coluna,coluna2,coluna3, (CASE WHEN vl_total = valor_pago THEN 'S' else 'N' end) as pago

Eu acredito, que toda operação de cálculo que você consiga fazer no SQL, é bom fazer.
O restante você trata diretamente. Ja tentou usar outro tipo de coleção ao inves do ArrayList?

B

bomba544:
Porque não tratar se foi pago ou não, diretamente na consulta? Algo do tipo:

SELECT coluna,coluna2,coluna3, (CASE WHEN vl_total = valor_pago THEN 'S' else 'N' end) as pago

Eu acredito, que toda operação de cálculo que você consiga fazer no SQL, é bom fazer.
O restante você trata diretamente. Ja tentou usar outro tipo de coleção ao inves do ArrayList?

Bom, vai ser meio punk porque preciso fazer 3 coisas:
1 - O número de taxas emitidas, tipo um count geral mas só para DISTINCT (CDCATEGORIA, NUINSCPROF, CDTIPO, CDTAXA)
2 - Dentre essas emitidas, quais das DISTINCT (CDCATEGORIA, NUINSCPROF, CDTIPO, CDTAXA) em que todos registros dela possuem CDSTATUSP != NULL (pagas)
3 - e quais dessas DISTINCT possuem ao menos um CDSTATUSP = NULL (não paga)

Não consigo imaginar isso numa SQL, ficaria tenso, não achas?
(Obs.: neste momento estou tentando ver um jeito de fazer essa SQL = tô pirando) kk

B

É muita informação pra entender exatamente o que você trazer na consulta. Mas pelo que vi, dois dos três que você citou, dá de fazer no SQL sim.
Qualquer coisa chama pvt etento te ajudar, aí depois postamos a solução aqui…

B

bomba544:
É muita informação pra entender exatamente o que você trazer na consulta. Mas pelo que vi, dois dos três que você citou, dá de fazer no SQL sim.
Qualquer coisa chama pvt etento te ajudar, aí depois postamos a solução aqui…

Mandei o PVT, pra galera ajudar se possível, lá vai aqui também:

Vou passar a SQL para criar a tabela e registrar dados de teste:

CREATE TABLE boletosTeste (
NUINSCPROF varchar( 7 ) DEFAULT NULL ,
CDCATEGORIA varchar( 2 ) DEFAULT NULL ,
CDTIPO varchar( 2 ) DEFAULT NULL ,
CDTAXA int( 11 ) DEFAULT NULL ,
NUPARCELA int( 11 ) DEFAULT NULL ,
CDSTATUSP int( 11 ) DEFAULT NULL
)

INSERT INTO boletosteste (NUINSCPROF, CDCATEGORIA, CDTIPO, CDTAXA, NUPARCELA, CDSTATUSP) VALUES
(‘0001499’, ‘01’, ‘04’, 0, 0, -1),
(‘0001499’, ‘01’, ‘04’, 0, 0, -1),
(‘0001499’, ‘01’, ‘04’, 0, 1, 1),
(‘0001499’, ‘01’, ‘04’, 0, 2, NULL),
(‘0001499’, ‘01’, ‘04’, 0, 3, 1),
(‘0001499’, ‘01’, ‘04’, 0, 4, 1),
(‘0001499’, ‘01’, ‘04’, 0, 5, 1),
(‘0001515’, ‘01’, ‘04’, 0, 0, 1),
(‘0001515’, ‘01’, ‘04’, 0, 0, 1),
(‘0001515’, ‘01’, ‘04’, 0, 1, -1),
(‘0001515’, ‘01’, ‘04’, 0, 2, -1),
(‘0001515’, ‘01’, ‘04’, 0, 3, -1),
(‘0001515’, ‘01’, ‘04’, 0, 4, -1),
(‘0001515’, ‘01’, ‘04’, 0, 5, -1),
(‘0001525’, ‘01’, ‘04’, 0, 0, NULL),
(‘0001525’, ‘01’, ‘04’, 0, 0, NULL),
(‘0001525’, ‘01’, ‘04’, 0, 1, NULL),
(‘0001525’, ‘01’, ‘04’, 0, 2, NULL),
(‘0001525’, ‘01’, ‘04’, 0, 3, NULL),
(‘0001525’, ‘01’, ‘04’, 0, 4, NULL),
(‘0001525’, ‘01’, ‘04’, 0, 5, NULL),
(‘0004997’, ‘01’, ‘04’, 0, 0, NULL),
(‘0004997’, ‘01’, ‘04’, 0, 0, NULL),
(‘0004997’, ‘01’, ‘04’, 0, 1, NULL),
(‘0004997’, ‘01’, ‘04’, 0, 2, NULL);

Agora que temos uma tabela igual dá pra conversar hehehe
Então, dentre essas 25 linhas de registros, temos 4 chaves diferentes.
A chave é formada por (NUMINSCPROF+CDCATEGORIA+CDTIPO+CDTAXA).

Cada chave dessa corresponde a um boleto emitido (por isso consigo resolver facilmente os emitidos), porém, desses 4:

a  chave foi paga parcialmente, por isso conta como 1 boleto não pago;

a  chave foi paga integralmente, conta como 1 pago;

a  e  chave não foram pagas, portanto conta como mais 2 não pagas.

Então foram 4 geradas, 3 não pagas e 1 paga.
Como fazer isso numa SQL? Ferrou! kkkk

adriano_si

Creio que essas ações de retirar da App e mandar para o SQL só vão melhorar um pouco seu problema. O problema maior, que é a demora de visualização ainda vai existir, pois um ArrayList desse tamanho SEMPRE será problemático.

Fazendo uma analogia com a vida real, até mesmo uma lista de vestibular com tudo isso de registro demoraria pra ser percorrida e exibida. A pergunta é, na sua visualização, você realmente precisa que todos os registros apareçam logo de primeira? Em minha opinião, tudo o que alcança esse Status, ou se transforma em pesquisa, ou é paginado. Sinceramente não vejo outra forma de solucionar esse problema sem gerar “transtornos” em algum ponto da App. :-/

B

adriano_si:
Creio que essas ações de retirar da App e mandar para o SQL só vão melhorar um pouco seu problema. O problema maior, que é a demora de visualização ainda vai existir, pois um ArrayList desse tamanho SEMPRE será problemático.

Fazendo uma analogia com a vida real, até mesmo uma lista de vestibular com tudo isso de registro demoraria pra ser percorrida e exibida. A pergunta é, na sua visualização, você realmente precisa que todos os registros apareçam logo de primeira? Em minha opinião, tudo o que alcança esse Status, ou se transforma em pesquisa, ou é paginado. Sinceramente não vejo outra forma de solucionar esse problema sem gerar “transtornos” em algum ponto da App. :-/

Adriano, o engraçado é que em PHP eu faço isso com vetores e não demora mais que 1 minuto para exibir o resultado esperado… Como que em JAVA, uma linguagem dita “mais poderosa”, não posso processar uma lista de 323.000 registros e depois “brincar” com eles?

Achei que o ArrayList fosse mais “robusto” pra esse tipo de coisa. Não há nada que possa ser mais indicado do que “dividir” os dados em paginações? Até por quê, iria dar mais dor de cabeça para manutenção do código no futuro…

Obs.: ainda acho que uma SQL que conseguisse responder as questões dadas no último post meu resolveria o problema…

B

Talvez não.
Como falei, já tentou usar outro tipo de coleção para tratar essa quantidade de registros?

B

bomba544:
Talvez não.
Como falei, já tentou usar outro tipo de coleção para tratar essa quantidade de registros?

Só usei LinkedHashMap, pois dentre as Maps é a única que mantêm a ordenação, mas ficou tão lento quanto… Você sugere alguma collection que eu possa usar para esse caso?

Abs.

rmendes08

O problema da demora é o seu algoritmo para contar taxas pagas e não pagas, que é da ordem de O(n³). Veja só:

for(Boleto bol : boletos){  //você abre um for para percorre uma lista de bolets +- 2.000.000 como você disse
     //...
    
     boletosChave = bol.getChavesIguais(chaveFor,boletos);  //daí, para cada boleto você faz uma nova busca na mesma lista buscando chaves repetidas
     
         varreBoletos = b.getChavesIguais(chaveFor,boletos);  //e mais uma vez, para cada boleto outra busca linear

ora, se você aninha 3 buscas lineares em uma lista da ordem de 10⁶ registros o total de operações é da ordem de 10¹⁸ operações. Isso é demorado para qualquer linguagem em qualquer máquina.

rmendes08

Outra pergunta, se (NUMINSCPROF,CDCATEGORIA,CDTIPO,CDTAXA) é uma chave, porque essa tupla se repete ?

B

rmendes08, boa pergunta. Essa tupla se repete pois, a “chave” é um código que faz referência à uma taxa, e cada registro dessa taxa, corresponde à uma parcela da taxa (boleto). A taxa em si é o que se forma dessa tupla, porém, há vários registros. Como primeira parcela, segunda, terceira, integral e etc…

rmendes08:
O problema da demora é o seu algoritmo para contar taxas pagas e não pagas, que é da ordem de O(n³).

ora, se você aninha 3 buscas lineares em uma lista da ordem de 10⁶ registros o total de operações é da ordem de 10¹⁸ operações. Isso é demorado para qualquer linguagem em qualquer máquina.

Muito bem observado rmendes08. Também percebi que é aí que está meu gargalo, porém não consegui sair dele por enquanto. Dado o cenário abaixo, bem simples, como conseguir:

1 - todos os boletos emitidos, sendo que cada chave representa 1 boleto emitido (A chave é formada por NUMINSCPROF+CDCATEGORIA+CDTIPO+CDTAXA).
2 - desses emitidos, os boletos pagos integralmente, sendo que para cada chave, é preciso que todos os CDSTATUSP estejam diferente de NULL.
3 - desses emitidos, os boletos não pagos, uma vez que para ser considerado não pago, ao menos um registro da chave deve possuir CDSTATUSP = NULL

CREATE TABLE `boletosTeste` ( 
`NUINSCPROF` varchar( 7 ) DEFAULT NULL , 
`CDCATEGORIA` varchar( 2 ) DEFAULT NULL , 
`CDTIPO` varchar( 2 ) DEFAULT NULL , 
`CDTAXA` int( 11 ) DEFAULT NULL , 
`NUPARCELA` int( 11 ) DEFAULT NULL , 
`CDSTATUSP` int( 11 ) DEFAULT NULL 
) 

INSERT INTO `boletosteste` (`NUINSCPROF`, `CDCATEGORIA`, `CDTIPO`, `CDTAXA`, `NUPARCELA`, `CDSTATUSP`) VALUES 
('0001', '01', '04', 0, 0, -1), 
('0001', '01', '04', 0, 0, -1), 
('0001', '01', '04', 0, 1, 1), 
('0001', '01', '04', 0, 2, NULL), 
('0001', '01', '04', 0, 3, 1), 
('0001', '01', '04', 0, 4, 1), 
('0001', '01', '04', 0, 5, 1), 
('00015', '01', '04', 0, 0, 1), 
('00015', '01', '04', 0, 0, 1), 
('00015', '01', '04', 0, 1, -1), 
('00015', '01', '04', 0, 2, -1), 
('00015', '01', '04', 0, 3, -1), 
('00015', '01', '04', 0, 4, -1), 
('00015', '01', '04', 0, 5, -1), 
('00015', '01', '04', 0, 0, NULL), 
('00015', '01', '04', 0, 0, NULL), 
('00015', '01', '04', 0, 1, NULL), 
('00015', '01', '04', 0, 2, NULL), 
('00015', '01', '04', 0, 3, NULL), 
('00015', '01', '04', 0, 4, NULL), 
('00015', '01', '04', 0, 5, NULL), 
('00049', '01', '04', 0, 0, NULL), 
('00049', '01', '04', 0, 0, NULL), 
('00049', '01', '04', 0, 1, NULL), 
('00049', '01', '04', 0, 2, NULL
);

Para compreender melhor, dado os dados acima, as respostas automatizadas devem ser:
4 boletos gerados.
3 não foram pagos.
1 pago.

Obs.: não estou pedindo a resposta, peço a lógica em Java, já que não consigo fugir da varredura por chaves, gargalo encontrado por você no meu script…

rmendes08

e STATUS = -1 , o que siginifica ?

rmendes08

Consulta SQL que já traz isso pronto:

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

Se PARCELAS_PAGAS = PARCELAS entã o boleto foi pago integralmente, não ?

Obs: crie um índice para a chave NUINSCPROF, CDCATEGORIA, CDTIPO, CDTAXA, porque uma tabela de 2mi registros sem índice será demorada mesmo.

pmlm

Todos os pagos:

SELECT distinct NUINSCPROF,CDCATEGORIA,CDTIPO,CDTAXA from boletosTeste t1 where not exists (SELECT 1 from boletosTeste t2 where t2.CDSTATUSP is null and t1.NUINSCPROF=t2.NUINSCPROF and t1.CDCATEGORIA=t2.CDCATEGORIA and t1.CDTIPO=t2.CDTIPO and t1.CDTAXA=t2.CDTAXA )
Todos os não pagos:

SELECT distinct NUINSCPROF,CDCATEGORIA,CDTIPO,CDTAXA from boletosTeste where CDSTATUSP is null
B

Status -1 é como se fosse 1, 0, 99, ou qualquer outro número, o que difere se é pago ou não é o Status ser NULL (não pago) ou diferente de NULL (pago)

rmendes08:
Consulta SQL que já traz isso pronto:

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

Se PARCELAS_PAGAS = PARCELAS entã o boleto foi pago integralmente, não ?

Obs: crie um índice para a chave NUINSCPROF, CDCATEGORIA, CDTIPO, CDTAXA, porque uma tabela de 2mi registros sem índice será demorada mesmo.

Isso, se NUM_PARCELAS = NUM_PARCELAS_PAGAS, então OK, está pago.
No entanto acho que esse THEN não funciona em MySQL, rodei o comando aqui e deu:
#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 ‘THEN 1 ELSE 0) as PARCELAS_PAGAS, FROM boletosteste GROUP BY NUINSCPROF, CDC’ at line 4

[quote=pmlm]Todos os pagos:

SELECT distinct NUINSCPROF,CDCATEGORIA,CDTIPO,CDTAXA from boletosTeste t1 where not exists (SELECT 1 from boletosTeste t2 where t2.CDSTATUSP is null and t1.NUINSCPROF=t2.NUINSCPROF and t1.CDCATEGORIA=t2.CDCATEGORIA and t1.CDTIPO=t2.CDTIPO and t1.CDTAXA=t2.CDTAXA )
Todos os não pagos:

SELECT distinct NUINSCPROF,CDCATEGORIA,CDTIPO,CDTAXA from boletosTeste where CDSTATUSP is null

pmlm, sua SQL de PAGOS travou meu MySQL quando executado na base de dados original (323.000 registros) e a de NÃO PAGOS retornou 25,699 registros, quando o correto deveria ser 14860 pela lógica que fiz em PHP mas não consigo replicar pra Java…

É… o esquema tá punk!

rmendes08

Corrija a query por favor:

CASE WHEN CDSTATUSP IS NOT NULL THEN 1 ELSE 0
B

[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:

B

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

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

B

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:

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

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)

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…

B

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…

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…

B

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.

B

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.

rmendes08

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.

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.

pmlm

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.

B

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  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  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  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   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,  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  FOR						
		}//fim  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;
pmlm

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);
pmlm

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;
B

Vou testar e já posto… Abraços.

B

[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

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.
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:

pmlm

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.


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…

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.

B

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;        
    }
adriano_si

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

pmlm

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

B

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.

pmlm

Continuas a deixar o dao para a parte de consulta de dados. Simplesmente em vez de devolveres uma listagem simples de dados, devolves dados agrupados por chaves.

B

Fechou, coloquei os códigos junto e não mudou significativamente, mas talvez tenha ficado mais claro de modo geral.

Pessoal, valeu o esforço, vocês estão de parabéns pela ajuda! Consegui fazer de um jeito ainda melhor que PHP, é disso que me referia, tinha de ter um jeito hehehe

Um abraço a todos e tudo de bom!

pmlm

Não é so colocar junto, deverás ficar com menos um ciclo.

Criado 18 de novembro de 2014
Ultima resposta 9 de dez. de 2014
Respostas 42
Participantes 6