Fórum Obter ID dos itens selecionados em um CheckListBox #623649
06/02/2025
0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var Qry : TIBQuery; begin Qry := TIBQuery . Create( nil ); try Qry . Database := FrmDm . dbBoletos; Qry . SQL . Clear; Qry . SQL . Add( 'SELECT CODIGO, NOME_PROJETO FROM TBNOMEPROJETO' ); Qry . SQL . Add( 'ORDER BY NOME_PROJETO' ); Qry . Open; while not Qry . eof do begin clbCentroCusto . Items . AddObject(Qry . FieldByName( 'NOME_PROJETO' ).AsString, TObject( Integer (Qry . FieldByName( 'CODIGO' ).AsInteger))); Qry . Next; end ; finally FreeAndNil(Qry); end ; end ; |
Em seguida, preciso obter a ID dos itens marcados, para então fazer um select in
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var i: Integer ; lista : tstringlist; begin try lista . create; for i := 0 to clbCentroCusto . Count - 1 do if clbCentroCusto . Checked[i] then begin lista . add(IntToStr( Integer (cbCentroCusto . Items . Objects[cbCentroCusto . ItemIndex]))); end ; finally lista . free; end ; |
Cheguei até este ponto, gostaria de pedir umas dicas, se estou no caminho certo.
obrigado.

Renan
Curtir tópico
+ 0Post mais votado
08/02/2025
Para que o plano de acesso fique razoavelmente eficiente, a tabela TBDUPLICATAS precisa ser indexada por (COD_PROJETO, VENCIMENTO).
Arthur Heinrich

Gostei + 1
Mais Posts
06/02/2025
Arthur Heinrich
A partir dele você precisa montar sua query.
Uma coisa que muita gente faz é montar a query dinamicamente, concatenando os IDs: select ... where ID in (valor1, valor2, ..., valorn) ...
Embora isto seja de fácil implementação, causa uma série de problemas.
1 - SQL Injection: Embora nesta caso, por se tratar de IDs numéricos, não vai quebrar o código e, por vir de uma seleção controlada, o usuário não terá condições de estipular IDs não presentes na lista, nas é sempre bom tomar cuidado com concatenações em queries, com base em valores digitados pelos clientes.
2 - Limite dos bancos: Dependendo da situação, é possível que o usuário selecione um número muito grande de itens. No Oracle, por exemplo, um filtro do tipo "coluna in (lista)" é limitado a listas com, no máximo, 1000 itens. Se este limite é ultrapassado, a query aborta.
Outro problema é relacionado ao plano de acesso. Geralmente, o plano vai utilizar o código informado na lista e executar um processo chamado "in list iterator", onde o plano de acesso é executado para cada um dos códigos e o resultado é concatenado. Uma lista com 100 valores, literalmente executa o plano de acesso 100 vezes.
3 - Hard Parse: Ao concatenar valores à query, sem o uso de bind variables, ou utilizar listas contendo número de valores distintos, o banco interpreta a query como uma query nova e precisa realizar o hard parse, que é muito mais lento que o soft parse, que consiste em identificar a query no cache e reexecutar o plano de acesso gerado previamente. Isto acarreta problemas de performance.
Uma alternativa, para tornar a query única, seria utilizar uma tabela temporária para armazenar os itens desejados e, executar a query utilizando um join com a tabela temporária. Desta forma, a query é sempre a mesma, mudando apenas o conteúdo da tabela temporária, que precisa ser populada previamente. Isto evita problemas de hard parse, melhora o cache e elimina o limite de 1000 itens. Entretanto, caso o número de itens escolhido retorne uns 10% a 15% do total de registros, é possível que utilize um plano de acesso ineficiente.
Gostei + 0
07/02/2025
Renan
Obrigado pelas explicações, muito interessante os pontos que você citou.
No meu caso, o usuário não vai digitar os códigos que estarão presentes no SQL IN.
Os dados serão obtidos a partir dos itens que o usuário marcar no CheckListBox.
Como você mencionou, a variável Lista vai armazenar as IDS marcadas.
Montei a SQL desta forma, mas estou com erro: Incompatible types: 'string' and 'TStringList'
1 2 3 4 5 6 7 8 9 10 11 12 | Query1 . Close; Query1 . SQL . Clear; Query1 . SQL . Add( 'SELECT D.NF, D.DATA_DOC, D.NUM_DOC, D.TOTAL, D.VENCIMENTO, D.DT_PAGAMENTO, D.OBS, C.CEDENTE' ); Query1 . SQL . Add( 'FROM TBDUPLICATAS AS D' ); Query1 . SQL . Add( 'INNER JOIN TBCEDENTES AS C ON C.CODIGO = D.COD_CEDENTE' ); Query1 . SQL . Add( 'WHERE D.VENCIMENTO >= :INI and D.VENCIMENTO <= :FIM' ); Query1 . SQL . Add( 'AND D.COD_PROJETO IN (' +Lista+ ')' ); //erro nesta linha Query1 . SQL . Add( 'ORDER BY D.VENCIMENTO' ); Query1 . ParamByName( 'INI' ).AsDate := dtpInicio . Date; Query1 . ParamByName( 'FIM' ).AsDate := dtpFim . Date; Query1 . Prepare; Query1 . Open; |
Gostei + 0
07/02/2025
Renan
Após criar a Lista eu delimito ela, com uma virgula
1 2 | Lista . Delimiter := ',' ; Lista . StrictDelimiter := True ; |
e na SQL
1 | Query1 . SQL . Add( 'AND D.COD_PROJETO IN (' +Lista . DelimitedText+ ')' ); |
Rodando normal, pelos testes que fiz até o momento.
Gostei + 0
Clique aqui para fazer login e interagir na Comunidade :)