Ayuda Dbsetfilter anidado

Ayuda Dbsetfilter anidado

Postby Garbi » Tue Oct 04, 2011 6:00 pm

Quiero filtrar una base de datos con distintas caracteristicas, si lo hago solo funciona correctamente el problema es cuando lo "anido" que no hace ningún filtro, os dejo el codigo por si podeis echarme una mano.
Gracias
Code: Select all  Expand view
static function filtrararticulo(aVars)
    if Var(1) <= 1 .and. Var(2) <= 1 .and. Var(3) <= 1 .and.;
       Var(4) <= 1 .and. Var(5) <= 1 .and. Var(5) <= 1
       msgalert("Debe seleccionar al menos una caracteristica del Articulo.")
       return ""
    endif
    (carticulo) ->( dbClearFilter(NIL) )
    cfiltro:=""
    IF Var(1) > 1
      * (carticulo)->(DbSetFilter(;
      *                  { || (carticulo)->ancho=val(tancho[Var(1)]) },;
      *                      "(carticulo)->ancho=val(tancho[Var(1)])"))
      cfiltro:=cfiltro+'"(carticulo)->ancho=val(tancho[Var(1)])"'
    endif
    IF Var(2) > 1
     * (carticulo)->(DbSetFilter(;
     *                   { || (carticulo)->densidad=(tdensidad[Var(2)]) },;
     *                       "(carticulo)->densidad=(tdensidad[Var(2)])"))
      if !empty(cfiltro)
         cfiltro:=cfiltro+'" .and. "'
      endif
      cfiltro:=cfiltro+'" (carticulo)->densidad=(tdensidad[Var(2)]) "'
    endif

       (carticulo)->(DbSetFilter( {|| &cfiltro }, cfiltro ) )
       (carticulo)->(dbgotop())

return nil
 
Saludos,
Regards,

Jose Luis Alepuz
joseluis@mancomputer.com
www.mancomputer.com
Garbi
 
Posts: 291
Joined: Wed Nov 02, 2005 3:28 pm

Re: Ayuda Dbsetfilter anidado

Postby hmpaquito » Tue Oct 04, 2011 6:18 pm

Amigo Garbi,

¿ Qué quieres, dos anidamientos, doscientos o dos mil ? Los que quieras :wink:

Mira el ejemplo:
Code: Select all  Expand view
FUNCTION Sample()
Local bFil1:= {|| .t. },;
         bFil2:= {|| .t.},;
         bFil3:= {|| .t.},;
         bFil4:= {|| .t.}
IF ...
   bFil1:= {|| lCon1}
ENDIF
IF ...
  bFil2:= {|| lCon2 }
ENDIF
IF ...
   bFil3:= {|| lCon3 }
ENDIF
IF ...
   bFil4:= {|| lCon4}
ENDIF
SELECT (cAli)
SET FILTER TO Eval(bFil1) .AND. Eval(bFil2) .AND. Eval(bFil3) .AND. Eval(bFil4)
RETURN NIL


Esto te va a ahorrar el rollete de estar duplicando la condicion la caracter y el bloque y ademas te evita que sea en tiempo de ejecucion cuando te cante que la sintaxis de la condicion (la caracter) esta mal.

Para tu caso asi funcionara, pero el problema que tiene este sistema: no se puede guardar la condicion de filtro para posteriormente restaurarla; quiza en tu ejemplo pasa exactamente lo mismo.

Fuera aparte de tu problema:
En general no me gusta el uso de SET FILTER porque es dificil cambiarlo y restaurarlo. Para ese caso lo que hago es que en el filtro incluyo una variable publica que sera la que encendere o apagare segun necesite apagar o encender el filtro.

Saludos
hmpaquito
 
Posts: 1482
Joined: Thu Oct 30, 2008 2:37 pm

Re: Ayuda Dbsetfilter anidado

Postby Garbi » Wed Oct 05, 2011 10:57 am

ok. lo he hecho con dbsetfilter() en lugar de set filter pero funciona de miedo. Gracias.
Saludos,
Regards,

Jose Luis Alepuz
joseluis@mancomputer.com
www.mancomputer.com
Garbi
 
Posts: 291
Joined: Wed Nov 02, 2005 3:28 pm

Re: Ayuda Dbsetfilter anidado

Postby Carlos Mora » Wed Oct 05, 2011 11:47 am

Solución alternativa, para no usar evals:

Code: Select all  Expand view

static function filtrararticulo(aVars)
    LOCAL cFiltro, aFiltros, i
    if Var(1) <= 1 .and. Var(2) <= 1 .and. Var(3) <= 1 .and.;
       Var(4) <= 1 .and. Var(5) <= 1 .and. Var(5) <= 1
       msgalert("Debe seleccionar al menos una caracteristica del Articulo.")
       return ""
    endif
    (carticulo) ->( dbClearFilter(NIL) )
    cfiltro:=""
    aFiltros:= {}
    IF Var(1) > 1
      aAdd( aFiltros, cArticulo+'->ancho=='+Ltrim(Str(tancho[Var(1)]))
    ENDIF
    IF Var(2) > 1
      aAdd( aFiltros, cArticulo+'->densidad=='+Ltrim(Str(tdensidad[Var(2)]))
    ENDIF
    // .... completar los 5 ifs
    cFiltro:= aFiltros[1]
    FOR i:= 2 TO Len(aFiltros)
        cFiltro+= '.AND.'+aFiltros[i]
    NEXT

    (carticulo)->(DbSetFilter( &('{||'+cfiltro+'}'), cfiltro ) )
    (carticulo)->(dbgotop())

return nil

 


con esto lo unico que no es constante en el filtro son los propios campos, ni hay evals ni macros salvo al construir el codeblock. Y el filtro se puede salvar y restaurar ya que no depende de variables.
Saludos
Carlos Mora
http://harbouradvisor.blogspot.com/
StackOverflow http://stackoverflow.com/users/549761/carlos-mora
“If you think education is expensive, try ignorance"
Carlos Mora
 
Posts: 988
Joined: Thu Nov 24, 2005 3:01 pm
Location: Madrid, España

Re: Ayuda Dbsetfilter anidado

Postby hmpaquito » Wed Oct 05, 2011 12:26 pm

Estimado Carlos,

Es cierto que el Eval() para filtrar tiene un coste... pero da unas posibilidades que la creacion de la condicion literal no da: se pueden hacer complicadisimas condiciones sin tener que estar construyendo la condicion literal: imaginate si en la condicion tienes que incluir que un campo este contenido en un array ¿ qué hacer ? ¿declarar el array como Private ?; ademas que en la construccion literal el compilador no va a detectar un error de sintaxis hasta el momento en que la condicion es macroevaluada &("{||"+ cFiltro+ "}" ), pero ojo TODA la condicion porque al ser una condicion "condicionada" no esta completa.

Me explico: tu pondrias:
Code: Select all  Expand view

Local cVar1:= "algo pasa con mary",;
         dVar2:= Date()
         nVar3:= 12345

aFiltro:= {}
IF lCon1
    Aadd(aFiltro, "CamCar == "+ "'"+ cVar1+ "'")
ENDIF
IF lCon2
    Aadd(aFiltro, "CamFec == CToD("+ DToC(dVar2)+ ")")
ENDIF
IF lCon3
    Aadd(aFiltro, "CamNum == "+ Str(nVar3, 14, ????????))
ENDIF
cFiltro:= aFiltro[1]
FOR nI:= 2 TO Len(aFiltro)
   cFiltro+= ".OR."+ aFiltro[nI]
NEXT
RETURN &("{||"+ cFiltro+ "}")
 

Este sistema requiere ir transformando cada variable del filtro al tipo apropiado, de manera que cualquier cambio en las variables puede influir en la condicion... vease el numero de decimales para los numericos.


Code: Select all  Expand view

Local cVar1:= "algo pasa con mary",;
         dVar2:= Date()
         nVar3:= 12345
Local bFil1:= bFil2:=  bFil3:= {|| .t. },;
         bFiltro:= {|| Eval(bFil1) .AND. Eval(bFil2) .AND. Eval(bFil3) }

IF lCon1
    bFil1:= {|| CamCar == cVar1}
ENDIF
IF lCon2
    bFil2:= {|| CamFec == dVar2 }
ENDIF
IF lCon3
    bFil3:= {|| CamNum == nVar3 }
ENDIF
RETURN bFiltro
 


El segundo metodo tiene la ventaja que aunque durante las pruebas que hagamos no se cumpla alguna de las condiciones, si hay una expresion sintacticamente erronea, no habra error de sintaxis en tiempo de ejecucion cuando SI se cumpla aquella condicion, porque el compilador habra "revisado" las condiciones, porque no son literales.


Repito... es cierto que hay un coste en el Eval()

Un afectuoso saludo
hmpaquito
 
Posts: 1482
Joined: Thu Oct 30, 2008 2:37 pm

Re: Ayuda Dbsetfilter anidado

Postby Carlos Mora » Wed Oct 05, 2011 2:39 pm

No veo necesario declarar el array como private, todo depende de la situación. En el ejemplo que pone el compañero, me gusta más la solución de la expresión, porque aunque requiera elaborar la expresión como string, 1)me permitiría ponerla y quitarla, 2)es más rápida seguro, 3)funcionaría con ADS (problema que suelo tener que resolver).
Y el array se puede poner como string, tal vez parezca más complicado, pero las ventajas lo compensan.
Si hay un error al elaborar la expresión que solo se vé en runtime, bueno... tu tampoco sabes de antemano si estas comparando los tipos correctos de los campos, cuyo tipo solo se puede saber en ejecución.
Y si hay un error, tarde o temprano vas a tener que corregirlo :D
Es cuestión de gustos, y de las ganas que uno tenga a la hora de currartelo ;)

Por cierto, de donde eres? No lo pone en tu perfil
Saludos
Carlos Mora
http://harbouradvisor.blogspot.com/
StackOverflow http://stackoverflow.com/users/549761/carlos-mora
“If you think education is expensive, try ignorance"
Carlos Mora
 
Posts: 988
Joined: Thu Nov 24, 2005 3:01 pm
Location: Madrid, España


Return to FiveWin para Harbour/xHarbour

Who is online

Users browsing this forum: No registered users and 92 guests