Fórum Obter ID dos itens selecionados em um CheckListBox #623649
06/02/2025
0
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
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'
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
Lista.Delimiter := ','; Lista.StrictDelimiter := True;
e na SQL
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 :)