Pesquisa Inteligente e Definitiva em uma base de dados DBF

Projeto [x]Harbour - Compilador de código aberto compatível com o Clipper.

Moderador: Moderadores

yugi386
Usuário Nível 2
Usuário Nível 2
Mensagens: 82
Registrado em: 24 Jul 2008 10:36
Localização: Minas Gerais

Pesquisa Inteligente e Definitiva em uma base de dados DBF

Mensagem por yugi386 »

Caros Colegas,

O Colega Eolo postou o seguiinte código relativo a eficiente função ORDWILDSEEK(). O Código segue abaixo:

Código: Selecionar todos

use produtos
set index to produtos
set orde to 1 // NOME do produto, caracter, tamanho 60
go top
priv lista:={}
?time() //-> início: 16:45:39
do while ordwildseek("*PESSEGO*",.t.)
  * procura produtos que contenham "PESSEGO" no nome, em qq posição,
  * do começo pro fim do arquivo = o argumento .T.
  * e adiciona esses nomes à matriz LISTA
  aadd(lista,field->nnome)
endd
?time() //-> final: 16:45:39 PO, NUM DEU TEMPO DE MUDAR O RELÓGIO!...
wait len(lista) // -> 138
achoice(05,11,22,70,lista,.t.,,)
quit
Eu criei, baseado em um trabalho de um amigo, a seguinte função de pesquisa:

Código: Selecionar todos

Procedure ACAO_FIL()     //      Filtro para A‡äes

*   Cadastro de A‡äes - Filtragem de Registros.

Local LC_T, LN_OPC:=0 
  
   * Definindo as Vari veis de Filtro:

    Local LAcao   := SPACE(50)         //  Nome da a‡Ćo
    Local LSetor  := SPACE(40)         //  Setor
    Local LEquip  := SPACE(40)         //  Equipamento
    Local LSubeq  := SPACE(40)         //  Sub-equipamento
    Local LPer1 := 0                   //  Periodicidade da a‡Ćo (inicial)
    Local LPer2 := 0                   //  Periodicidade da a‡Ćo (final)
    Local LDat1 := CTOD(" /  /    ")   //  Data do Cadastro (inicial)
    Local LDat2 := CTOD(" /  /    ")   //  Data do Cadastro (final)

    Save Screen to LC_T
    CRIABOX3D(08,04,22,73)  //  janela sem titulo

Do While .T.

    * Fun‡Ćo SubMenu:

    Set Color to W+/B
    @ 09,07 Say "    Filtrar Registros     "
    Set Color to N/N
    @ 10,05 SAY "  "
    Set Color to N/W
    @ 09,05 say "ÜÜ"

    LN_OPc := SubMenu("N/w,W+/R",;
                                   {" 1. Nome da a‡Ćo      ",;
                                   " 2. Setor             ",;
                                   " 3. Equipamento       ",;
                                   " 4. Sub-equipamento   ",;
                                   " 5. Perˇodo           ",;
                                   " 6. Data do cadastro  ",;
                                   " 7. Filtrar           ",;
                                   " 8. Mostrar todos     ",;
                                   " 9. Sair              "},;
                                  {"","","","","","","","",""},;
                                  10,07,"N/N*",07)

            inkey()
            if lastkey() == 27  //  ESC
                 LN_opc := 9
            Endif
    Do Case
        Case LN_Opc = 0      //     Filtrando os Registros.

            Pc_filtro := Space(0)

          * Preparando os dados para a filtragem:
          *-----------------------------------------------

              LAcao   := alltrim(XLETRA(Lacao,1))   //  Nome da a‡Ćo
              LSetor  := alltrim(XLETRA(Lsetor,1))  //  Setor
              LEquip  := alltrim(XLETRA(Lequip,1))  //  Equipamento
              LSubeq  := alltrim(XLETRA(Lsubeq,1))  //  Sub-equipamento

             * Abaixo temos as vari veis privadas que permitem
             * visualizar o conteŁdo do filtro:

             tLAcao   := Lacao             //  Nome da a‡Ćo
             tLSetor  := Lsetor            //  Setor
             tLEquip  := Lequip            //  Equipamento
             tLSubeq  := LSubeq            //  Sub-equipamento
             tLPer1 := Lper1               //  Periodicidade da a‡Ćo (inicial)
             tLPer2 := Lper2               //  Periodicidade da a‡Ćo (final)
             tLDat1 := Ldat1               //  Data do Cadastro (inicial)
             tLDat2 := Ldat2               //  Data do Cadastro (final)

          * L˘gica do Encadeamento de Filtros:
          *------------------------------------------

            * Verificando A€ĺES:    **********************

            If Len(Lacao) <> 0
                Pc_Filtro = "'" + LAcao + "'" + " $ ACAO "
            Endif

            * Verificando Setor:    **********************

            If Len(Lsetor) <> 0
                If Len(Pc_filtro) <> 0
                    Pc_Filtro = Pc_filtro + ".And. '" + Lsetor + "'" + " $ SETOR "
                Else
                    Pc_Filtro = Pc_filtro + "'" + Lsetor + "'" + " $ SETOR "
                Endif
            Endif

            * Verificando Equipamento:    **********************

            If Len(lequip) <> 0
                If Len(Pc_filtro) <> 0
                    Pc_Filtro = Pc_filtro + ".And. '" + lequip + "'" + " $ EQUIP "
                Else
                    Pc_Filtro = Pc_filtro + "'" + lequip + "'" + " $ EQUIP "
                Endif
            Endif

            * Verificando Sub-Equipamento:    **********************

            If Len(LSUBEQ) <> 0
                If Len(Pc_filtro) <> 0
                    Pc_Filtro = Pc_filtro + ".And. '" + LSUBEQ + "'" + " $ SUBEQ "
                Else
                    Pc_Filtro = Pc_filtro + "'" + LSUBEQ + "'" + " $ SUBEQ "
                Endif
            Endif

            * Verificando Periodicidade:    **********************

            If !Empty(LPER1) .and. !Empty(LPER2)
                If Len(Pc_filtro) <> 0
                    Pc_Filtro = Pc_filtro + ".And. PERIODO >= " + ALLTRIM(STR(LPER1)) +;
                            " .And. PERIODO <= " + ALLTRIM(STR(LPER2)) + " "
                Else
                    Pc_Filtro = Pc_filtro + " PERIODO >= " + ALLTRIM(STR(LPER1)) +;
                            " .And. PERIODO <= " + ALLTRIM(STR(LPER2)) + " "
                Endif
            Elseif !Empty(LPER1)
                If Len(Pc_filtro) <> 0
                    Pc_Filtro = Pc_filtro + ".And. PERIODO >= " + ALLTRIM(STR(LPER1)) + " "
                Else
                    Pc_Filtro = Pc_filtro + " PERIODO >= " + ALLTRIM(STR(LPER1)) + " "
                Endif
            Elseif !Empty(LPER2)
                If Len(Pc_filtro) <> 0
                    Pc_Filtro = Pc_filtro + ".And. PERIODO <= " + ALLTRIM(STR(LPER2)) + " "
                Else
                    Pc_Filtro = Pc_filtro + " PERIODO <= " + ALLTRIM(STR(LPER2)) + " "
                Endif
            Endif

            * Verificando Data de cadastro:    **********************

            If !Empty(ldat1) .and. !Empty(ldat2)
                If Len(Pc_filtro) <> 0
                    Pc_Filtro = Pc_filtro + ".And. DATACAD >= " + "Ctod('" + Dtoc(ldat1) + "')" +;
                            " .And. DATACAD <= " + "Ctod('" + Dtoc(ldat2) + "')"
                Else
                    Pc_Filtro = Pc_filtro + " DATACAD >= " + "Ctod('" + Dtoc(ldat1) + "')" +;
                            " .And. DATACAD <= " + "Ctod('" + Dtoc(ldat2) + "')"
                Endif
            Elseif !Empty(ldat1)
                If Len(Pc_filtro) <> 0
                    Pc_Filtro = Pc_filtro + ".And. DATACAD >= " + "Ctod('" + Dtoc(ldat1) + "')"
                Else
                    Pc_Filtro = Pc_filtro + " DATACAD >= " + "Ctod('" + Dtoc(ldat1) + "')"
                Endif
            Elseif !Empty(ldat2)
                If Len(Pc_filtro) <> 0
                    Pc_Filtro = Pc_filtro + ".And. DATACAD <= " + "Ctod('" + Dtoc(ldat2) + "')"
                Else
                    Pc_Filtro = Pc_filtro + " DATACAD <= " + "Ctod('" + Dtoc(ldat2) + "')"
                Endif
            Endif

            *************************************************
            * FILTRANDO:
            *************************************************

            If Len(Pc_Filtro) <> 0
                Ln_Opc = 0
                Set Filter to &Pc_Filtro
                Go top
                Exit
            Else
                Ln_opc = 8
                Set Filter To
                Go Top
                Exit
            Endif

        Case Ln_Opc = 1     //  Filtragem Por Nome  ***********************

            Setcolor("N/W,W+/R")
            Set CurSor On
            @ 11,33 say "" Get Lacao PICT "@S35"
            Read

        Case LN_OPc = 2     //  Filtragem por Setor  ***********************

            Setcolor("N/W,W+/R")
            Set CurSor On
            @ 12,33 say "" Get Lsetor PICT "@S35"
            Read

        Case LN_OPc = 3     //  Filtragem por Equipamento ***************

            Setcolor("N/W,W+/R")
            Set CurSor On
            @ 13,33 say "" Get Lequip PICT "@S35"
            Read

        Case LN_OPc = 4     //  Filtragem por Sub-Equipamento ***************

            Setcolor("N/W,W+/R")
            Set CurSor On
            @ 14,33 say "" Get LSubeq PICT "@S35"
            Read

        Case LN_OPc = 5     //  Filtragem Por periodicidade  ***********************

            Setcolor("N/W,W+/R")

            Set CurSor On
            @ 15,33 say ">=" Get lper1 pict "9999"
            @ 15,48 say "<=" Get lper2 pict "9999" Valid (lper2 >= lper1) .Or. Empty(lper2)
            Read

        Case LN_OPc = 6     //  Filtragem Por Data do Cadastro  ***********************

            Setcolor("N/W,W+/R")

            Set CurSor On
            @ 16,33 say ">=" Get LDat1
            @ 16,48 say "<=" Get LDat2 Valid (LDat2 >= LDat1) .Or. Empty(LDat2)
            Read

        Case Ln_Opc == 8    //  Sem Filtro

             * Vari veis para visualizar o conteŁdo do filtro:
             tLAcao   := SPACE(50)         //  Nome da a‡Ćo
             tLSetor  := SPACE(40)         //  Setor
             tLEquip  := SPACE(40)         //  Equipamento
             tLSubeq  := SPACE(40)         //  Sub-equipamento
             tLPer1 := 0                   //  Periodicidade da a‡Ćo (inicial)
             tLPer2 := 0                   //  Periodicidade da a‡Ćo (final)
             tLDat1 := CTOD(" /  /    ")   //  Data do Cadastro (inicial)
             tLDat2 := CTOD(" /  /    ")   //  Data do Cadastro (final)

            Pc_filtro := Space(0)
            Set Filter To
            Go top
            Exit

        Case Ln_Opc == 9    //  Abandona a fun‡Ćo sem mexer no filtro
                            //  dos registros
            Exit

    Endcase

Enddo

    Restore Screen from LC_T

    If Ln_opc == 8
     Setcolor("N/W")
     @ 24,43 say " Visualiza‡Ćo sem filtro"
    Elseif Ln_opc == 0
     Setcolor("N/W")
     @ 24,43 say " Visualiza‡Ćo com filtro"
    Endif

Return

Trata-se de um filtro genérico onde se pode estabelecer condições para qualquer filtragem misturando qualquer campo do arquivo DBF. O problema é que a função é baseada em SET FILTER e como já estamos entediados de saber ela é tremendamente lenta para um número maior de registros.

A questão crucial é: Seria possível utilizar a função ORDWILDSEEK() para realizar um trabalho semelhante ao que fiz com a função exposta no código anterior? Se alguém puder achar uma solução talvez esta seja a última rotina de pesquisa a ser criada para um DBF porque não se terá limites para criar filtros extrambóticos e extremamente rápidos.

Grato por qualquer manifestação.
jamazevedo
Usuário Nível 3
Usuário Nível 3
Mensagens: 122
Registrado em: 29 Dez 2005 16:50
Localização: Manaus - AM

Re: Pesquisa Inteligente e Definitiva em uma base de dados DBF

Mensagem por jamazevedo »

Bem vamos lá.

Acredito utópico uma pesquisa genérica como a que você propõe, por isso desenvolvi ao longo do tempo algumas técnicas de pesquisas manuais, já que SET FILTER com base de dados grande é extremamente lento.

No meu caso:

Cadastro de Situações: (fixo não muda)

Código: Selecionar todos

Código Descrição
01     Entrada
02     Avaliação
03     Orçamento
04     Aprovada
05     Consertada
06     Fechada
07     Cancelada
08     Baixada
Cadastro de Entradas: (variável a todo momento tem um novo registro)
- Tem um campo com o código da situação acima, então criei um índice para cada situação acima com a clausula FOR do comando INDEX.
- Então para filtrar rapidamente a base de entradas por situação é só mudar o índice.
- A desvantagem é a lista de índice que fica grande.

Ainda não use a função ORDWILDSEEK() pois quando preciso fazer uma pesquisa como a mostrada no exemplo, possuo uma rotina que vare o arquivo do inicio ao fim e filtra em um array os registro que interessam, é bem rápido.

Resumindo seria melhor você identificar qual a real necessidade e desenvolver rotinas especificas, porém sua idéia é excelente vou tentar desenvolver algo a respeito.
______________________________________________________
Usando: Linux Ubuntu 18.04, Harbour 3.2.0dev r1811161533
______________________________________________________
José Airton de Menezes Azevedo
Manaus - AM
Avatar do usuário
sygecom
Administrador
Administrador
Mensagens: 7131
Registrado em: 21 Jul 2006 10:12
Localização: Alvorada-RS
Contato:

Re: Pesquisa Inteligente e Definitiva em uma base de dados DBF

Mensagem por sygecom »

Olá yugi386,
Realmente, esse Milagre não existe. Mas existe algumas soluções que podem lhe ajudar a melhorar o desempenho, porem como tudo na vida, tem um custinho.
1º opção é coompila com ADS e usar no lugar do SET FILTER TO a função ADSSETAOF() que da uma melhorada no desempenho, porem o ADS se não me engano é free apenas para 4 ou 5 micros em rede, mas que isso tem que comprar a licença de uso do ADS SERVER ou algo com nome parecido, para saber bem certinho tem que entrar em contato com o representante do ADS aqui no Brasil a RENET.
2º opção é você migrar para um SQL da vida e passar a usar comandos SQL direto que seu desempenho vai ser otimo em local e rede.
3º opção é usar o BMDBFCDX que tem no Harbour e xHarbour, o ganho de desempenho é pouco mas se nota alguma coisa.
4º opção é o tal do RMDBFCDX esse sim se nota diferença, porem ele só se encontra no xHarbour comercial.

Boa sorte...
Leonardo Machado
xHarbour.org + Hwgui + PostgreSql
Responder