Acesso a Dados – IndRegua()

Introdução

Desde os primórdios do AdvPL, quando o ERP Microsiga ainda era um executável stand-alone, que usava arquivos DBF para armazenar os meta-dados (dicionários do ERP) e tabelas de dados da aplicação, foi criada uma função no FrameWork AdvPL, para encapsular e tratar a criação de um índice temporário para uma tabela DBF qualquer.

Embora esta função seja do FrameWork do AdvPL, ainda restam hoje algumas dúvidas sobre qual é a melhor forma de utilizá-la, inclusive quais são as diferenças de comportamento quando usamos a função em um alias de uma tabela usando ADS ou c-Tree, e o que ela faz de “diferente” quando é utilizada em uma tabela acessada pelo DBAccess em um SGBD relacional.

Comportamento original da IndRegua

A função foi desenhada para, a partir de um ALIAS ou WorkArea de uma tabela DBF aberta, permitir criar um índice temporário para a tabela, onde você deve informar uma condição de ordenação, usando a mesma sintaxe de composição de chaves de índice do AdvPL, pode ser especificada uma condição de filtro, para que o índice seja criado considerando apenas os registros do alias atual que atendam a condição de filtro especificada.

Com um alias de uma tabela DBF ou c-Tree, a função cria fisicamente no disco um índice de uso temporário (extensão .idx) atendendo as condições parametrizadas. Mesmo quando especificada uma condição de filtro, toda a tabela é percorrida internamente para a criação deste índice. Logo, o tempo de criação do índice filtrado é proporcional ao tamanho da tabela. Porém, uma vez criado e selecionado para uso, este índice permitia uma navegação praticamente instantânea sobre os dados filtrados e ordenados. Após o uso, este índice deve ser fechado, e removido do disco.

Este processo somente é indicado quando você precisa fazer a leitura completa dos dados filtrados mais de uma vez, como por exemplo para a exibição em tela de um Browse para marcar itens de processamento (MarkBrowse). Caso contrário, você poderia simplesmente aplicar um filtro na tabela, e percorrer os registros válidos apenas uma vez. Embora pudesse ser criada uma tabela temporária baseada em uma condição de filtro, e depois fazer o browse diretamente nesta tabela, caso o filtro fosse pouco restritivo, demoraria mais para criar a tabela temporária do que para fazer um índice filtrado, além de consumir muito espaço em disco.

Usando com uma tabela do DBAccess

A utilização da IndRegua no DBAccess, em uma tabela acessada pela RDD TOPCONN, não haverá a criação física explícita pela aplicação de um índice temporário. Na verdade, as condições de filtro e ordenação serão traduzidas pelo motor de navegação do DBAccess, e repassados ao SGBD durante a leitura dos dados. Logo, elimina-se logo de cara o tempo de espera pela criação explícita de um índice físico.

Porém, como estamos na prática submetendo por baixo queries no SGBD, o motor de execução SQL do Banco de Dados em questão vai “se virar” para selecionar os dados nas condições solicitadas, levando em conta as condições de filtro e a ordenação informada. Esta é uma prerrogativa do SDBD, onde ele avalia se é possível aproveitar um índice já existente no SGBD para selecionar os registros que atendem a condição de filtro informada. Caso não seja possível fazer isso, o SGBD vai dar um jeito de resolver a query, mas seu desempenho pode ser muito prejudicado de acordo com o tamanho da tabela. Naturalmente esta operação será muito bem executada pelo SGBD, caso exista algum índice no banco de dados que contemple todos os campos utilizados nas condições de filtro ou seleção de registros desejados.

Este comportamento do banco justifica por que determinadas condições de filtro, quando acrescentadas pelo usuário em um Browse do ERP padrão ficam muito lentas. Existem contornos operacionais para estes tipos de casos, onde normalmente um DBA utiliza um mecanismo de monitoramento de desempenho do próprio SGBD, para elencar as queries menos performáticas, onde normalmente a inclusão de um índice específico no SGDB para favorecer o processamento da query resolve a questão de desempenho.

Filtro ou Query

Nada impede que você faça uma Query para recuperar os dados que você deseja do banco, já colocando nela apenas as colunas desejadas, JOINS quando necessários, colocando as condições e ordenação desejadas. Vale a mesma regra do filtro: Caso exista um índice no SGBD que contemple os campos usados nas condições de seleção de registros (Where da query), mais fácil será para o SGBD processar e retornar os dados.

Uma Query vai ser naturalmente mais rápida que o filtro, pois ela será submetida apenas uma vez ao SGBD, enquanto um filtro vai inferir condições no motor de navegação de registros do DBAccess, que sob demanda vai submeter várias queries para identificar os blocos de dados a serem recuperados. Porém, esta diferença não é tão gritante. A engine de ISAM emulada é bem eficiente, e em muitos casos um filtro atende melhor às condições do Programa. Não é viável colocar um alias de uma Query, que somente permite movimentação para os próxios registros dentro de um Browse de visualização de interface. Dependendo do caso, alguns programas lêem o resultado da query e criam um arquivo temporário no disco, e isto têm o custo da leitura dos dados pela rede, e posterior envio de cada registro para gravação em disco. Mas isto é assunto para outro capítulo … eheheh ….

Boas práticas

Nas condições de filtro, você consegue “ajudar” o motor de queries do SGBD, quando você previamente identifica um índice no banco que contenha todos ou a maioria dos campos que você vai selecionar os dados, e especifica todas as condições de filtro dos dados na ordem de campos do índice. Por exemplo, se você vai recuperar apenas os dados sob condições que usam os campos CPO1, CPO2 e CPO3, e você têm um índice no seu SGBD cuja chave seja “CPO1,CPO3,CPO2”, você ajuda o banco na hora de escrever a sua query informando as condições nesta ordem: “CPO1 >= ’01’ AND CPO3 = ‘001’ AND CPO2 between ’01’ and ’05′”

Outra coisa importante, tanto em filtros como em Queries, é evitar ao máximo usar condições que concatenam valores de campos. Por exemplo, você tem uma variável na memória do Advpl, que contem a concatenação de dois campos, CPO2 e CPO3. Logo, ao escrever a query, você faz algo assim:

cQuery := "SELECT CPO1,CPO2,CPO3,CPO4,CPO5 FROM TABELA WHERE "
cQuery += "CPO1 = '01'"
cQuery += " AND (CPO2 || CPO3) = '"+cChave+"'"

Isto obriga o SGBD a criar internamente uma coluna calculada para então validar o critério de comparação. Para o SGBD, é MUITO mais leve você quebrar as condições:

cQuery := "SELECT CPO1,CPO2,CPO3,CPO4,CPO5 FROM TABELA WHERE "
cQuery += "CPO1 = '01'"
cQuery += " AND CPO2 = '"+left(cChave,2)+"'"
 cQuery += " AND CPO3 = '"+substr(cChave,3)+"'"

Na prática, uma IndRegua() em uma tabela do DBAccess possui basicamente um comportamento de filtro, permitindo especificar uma ordenação diferenciada. Logo, todas as regras de performance aplicáveis a um filtro de IndRegua() também se aplicam a um filtro definido pelo comando SET FILTER TO — ou pela função DbSetFilter().

Lembrando de um ponto importante do filtro: Por questões de compatibilidade, um filtro AdvPL definido via SET FILTER ou DbSetFilter() pode conter expressões AdvPL, incluindo operadores e funções não suportadas pelo SGDB. Porém não existe mágica, o Application Server manda o filtro pro DBAccess, e ele remonta o filtro considerando apenas as condições que o SGDB é capaz de resolver, e cada registro retornado pelo DBAccess ao Appllication Server é revalidado, e caso não seja válido pela expressão de filtro re-ececutada no AdvPL, o registro é ignorado e o próximo registro é solicitado, até que seja retornado um registro válido.

O pior cenário de desempenho e consumo de recursos é um filtro muito restritivo em uma tabela muito grande, pois muitos registros podem ser trafegados desnecessariamente ao Application Server até que os registros que atendem realmente a condição de filtro sejam percorridos pelo programa. Logo, é extremamente recomendável que o seu filtro tenha o menor número possivel de instruções que não possam ser processadas no SGDB. Quanto mais simples, melhor. Na documentação de referências no final deste artigo, a documentação da função DbSetFilter() aborda em detalhes vários destes aspectos.

Conclusão

Este artigo terá continuações, para abordar outros aspectos de acesso a dados, mas já é um bom começo. Ainda existem muitos espaços em branco para serem preenchidos com mais informações, espero ter ajudado de alguma forma. Acompanhem os próximos posts, têm muita coisa boa pra ser publicada 😀

Abraços e até o próximo post, pessoal 😉

Referências

http://tdn.totvs.com/pages/releaseview.action?pageId=6814909
http://tdn.totvs.com/display/tec/SET+FILTER+TO
http://tdn.totvs.com/display/tec/DBSetFilter

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s