A vueltas con los filtros avanzados

18/07/2006 - 00:44 por klomkbock | Informe spam
Hola a todos. Espero que los que esteis por otras latitudes no os esteis
derritiendo como los que andamos por estas, las hispanicas.

Aqui vuelvo de nuevo a abusar de vuestra generosidad.

Llevaba tiempo dandole vueltas a los filtros avanzados para rellenar un
combobox con los registros unicos de un campo variable(puede variar la
longitud del campo y el campo mismo). Al fin lo he conseguido.

El problema es que el motivo de usar los filtros era ganarle en velocidad
al proceso, que hasta ahora realizaba mediante un objeto collection,
cuando se trata de trabajar con varios miles de registros.

Por lo que se(de muy buena fuente en estas lides), el uso de filtros hace
mas rapida la ejecucion que el de colecciones, sin embargo, algo debo de
estar haciendo mal, pues de momento, y tras probar unas cuantas
posibilidades, el uso del objeto collection es bastante mas rapido que los
filtros.

Para 5000 registros, con filtro avanzado tarda entre 6 y 10 segundos,
mientras que con collection tarda un maximo de 2 segundos(si estos tiempos
fueran asi para hasta 20000 registros me podrian valer, pero me seguiria
quedando la espina de los filtros avanzados y de en que estoy fallando)

Al final expongo los codigos en ambos casos. Con los filtros expongo 3,
que tardan practicamente igual, pero antes explico su entorno brevemente??:

En los codigos intervienen:

=>a) tres hojas(en unos intervienen dos y tres en otros):

.-Hoja1= "Listado": contiene la lista con los datos. Para las pruebas
actuales 5000 registros. El unico campo que no puede tener registros
repetidos es el 1º(columna A) y, a partir del 5º(columna E), puede
contener registros vacios.
.-Hoja2= "Matrices": contiene listas para rellenar matrices o variables.
La que interviene en los codigos es la columna A que contiene un listado
con todas las letras de la A a la Z, que se corresponden con las columnas
de Listado (26 en total)
.-Hoja3= "Oculta": se usa para pegar los datos filtrados y pasarlos al
combo de destino.

=>b) dos combobox (que interesen al caso) de un formulario:

.-ComboBox1= "cmbElegir": contiene los titulos de los 26 campos de
"Listado" (rango "a1:z1"). Se cargan al inicializarse el formulario.
.-ComboBox2= "cmbCriterio": se rellena con los registros no repetidos del
campo elegido en "cmbElegir"(cmbElegir_Change).

==>> Este es el codigo con collection:

Private Sub cmbElegir_Change()
Dim rngListado As Range, xList As Long
Dim ListaUnicos As New Collection
Application.ScreenUpdating = False
With cmbElegir
If .Value = .BoundValue Then Exit Sub
cmbCriterio.Clear: lstSeleccionar.Clear
If .ListIndex < 0 Then Exit Sub Else nL = .ListIndex + 1
End With
ltC = Worksheets("Matrices").Cells(nL, 1)
With Worksheets("Listado")
If .AutoFilterMode Then .AutoFilterMode = False
fF = .[a65536].End(xlUp).Row
.Range("a1:z" & fF).Sort _
key1:=.Range(ltC & "1"), _
header:=xlYes
For Each rngListado In .Range(ltC & "2:" & ltC & fF)
On Error Resume Next
If Trim(rngListado) <> "" Then _
ListaUnicos.Add rngListado, CStr(rngListado)
On Error GoTo 0
Next
End With
For xList = 1 To ListaUnicos.Count
cmbCriterio.AddItem ListaUnicos(xList)
Next
Set ListaUnicos = Nothing
Application.ScreenUpdating = True
End Sub

==>> y estos con advanced filter

->1ªprueba

Private Sub cmbElegir_Change()
''Se filtra bien, pero mas bien lento _
'''(de 7 a 10 sg con 5000 filas)
Application.ScreenUpdating = False
With cmbElegir
If .Value = .BoundValue Then Exit Sub
cmbCriterio.Clear: lstSeleccionar.Clear
If .ListIndex < 0 Then Exit Sub Else nL = .ListIndex + 1
End With
ltC = Worksheets("Matrices").Cells(nL, 1)
With Worksheets("Oculta")
.UsedRange.EntireRow.Delete
With Worksheets("Listado")
If .AutoFilterMode Then .AutoFilterMode = False
fF = .[a65536].End(xlUp).Row
.Range("a1:z" & fF).Sort _
key1:=.Range(ltC & "1"), _
header:=xlYes
.Range(ltC & "1:" & ltC & fF).AdvancedFilter _
criteriarange:=.Range(ltC & "1:" & ltC & fF), _
Action:=xlFilterCopy, _
copytorange:=Worksheets("Oculta").Range("a1"), _
unique:=True
End With
fFo = .Range("a65536").End(xlUp).Row
If fFo < 2 Then Exit Sub
cmbCriterio.List = .Range("a2:a" & fFo).Value
End With
Application.ScreenUpdating = True
End Sub

->2ªprueba (quizas ligeramente mas rapido que el anterior pero no estoy seguro

Private Sub cmbElegir_Change()
Application.ScreenUpdating = False
With cmbElegir
If .Value = .BoundValue Then Exit Sub
cmbCriterio.Clear: lstSeleccionar.Clear
If .ListIndex < 0 Then Exit Sub Else nL = .ListIndex + 1
End With
With Worksheets("Oculta")
.UsedRange.EntireRow.Delete
With Worksheets("Listado")
If .AutoFilterMode Then .AutoFilterMode = False
fF = .[a65536].End(xlUp).Row
.Range("a1:z" & fF).Sort _
key1:=.Cells(1, nL), _
header:=xlYes
.Range(.Cells(1, nL), .Cells(fF, nL)).AdvancedFilter _
criteriarange:=.Range(.Cells(1, nL), .Cells(fF, nL)), _
Action:=xlFilterCopy, _
copytorange:=Worksheets("Oculta").Range("a1"), _
unique:=True
End With
fFo = .Range("a65536").End(xlUp).Row
If fFo < 2 Then Exit Sub
cmbCriterio.List = .Range("a2:a" & fFo).Value
End With
Application.ScreenUpdating = True
End Sub

->3ªprueba (como la 1ª pero usando algunas variables mas, tarda igual)

Private Sub cmbElegir_Change()
''Prueba con variables range y variant para ver si agiliza
'''pero sigue mas o menos igual
Dim rngListado As Range, rngFiltrado
Application.ScreenUpdating = False
With cmbElegir
' If .Value = .BoundValue Then Exit Sub
cmbCriterio.Clear: lstSeleccionar.Clear
If .ListIndex < 0 Then Exit Sub Else nL = .ListIndex + 1
End With
ltC = Worksheets("Matrices").Cells(nL, 1)
With Worksheets("Oculta")
.UsedRange.EntireRow.Delete
With Worksheets("Listado")
If .AutoFilterMode Then .AutoFilterMode = False
fF = .[a65536].End(xlUp).Row
.Range("a1:z" & fF).Sort _
key1:=.Range(ltC & "1"), _
header:=xlYes
Set rngListado = .Range(ltC & "1:" & ltC & fF)
rngListado.AdvancedFilter _
criteriarange:=rngListado, _
Action:=xlFilterCopy, _
copytorange:=Worksheets("Oculta").Range("a1"), _
unique:=True
End With
fFo = .Range("a65536").End(xlUp).Row
If fFo < 2 Then Exit Sub
rngFiltrado = .Range("a2:a" & fFo).Value
cmbCriterio.List = rngFiltrado
End With
Application.ScreenUpdating = True
End Sub

Bueno, como siempre me ha salido otro tomo.

Si podeis echarme una mano os lo agradezco.

En cualquier caso, un saludo y hasta pronto.
Ivan

Preguntas similare

Leer las respuestas

#1 Héctor Miguel
18/07/2006 - 08:56 | Informe spam
hola, Ivan !

... tiempo dandole vueltas a los filtros avanzados para rellenar un combobox con los registros unicos de un campo variable...
... el motivo de usar los filtros era ganarle en velocidad al proceso, que hasta ahora realizaba mediante un objeto collection...
... algo debo de estar haciendo mal, pues de momento, y tras probar unas cuantas posibilidades
... el uso del objeto collection es bastante mas rapido que los filtros.
... 5000 registros, con filtro avanzado tarda entre 6 y 10 segundos... con collection tarda un maximo de 2 segundos
... para hasta 20000 registros me podrian valer, pero me seguiria quedando la espina de los filtros avanzados y de en que estoy fallando [...]



1) el siguiente ejemplo, probado con ~ 30000 registros en las columnas 'A:Z' de una hoja 'Listado', de las cuales...
[segun la columna 'elegida' en un cmbElegir] pudieran 'devolver' entre 300 a 2000 registros 'unicos'...
se ha tardado [en llenar un cmbCriterio] algo asi como... 'un suspiro' :))

2) los 'supuetos' son...
a) la 'funcion' del listindex del cmbElegir es 'solo' para asignar la literal de la columna 'seleccionada' [tu variable ltC]
b) las columnas 'utiles' de la hoja 'Listado' son -exclusivamente- de la 'A' a la 'Z' [el resto... esta 'disponible'] -?- <= OJO
c) [de lo anterior]... se deja libre la columna 'AA' y se aprovecha la columna 'AB' para el rango de extraccion [filtros avanzados]
d) como -se supone que- el resultado de la extraccion NO dejara celdas 'vacias'... la ordenacion se hace sobre el resultado [mas rapido]

3) 'te toca'... hacer las 'prevenciones' necesarias para cuando no haya 'unicos' a devolver :))

corre algunas pruebas... compara la velocidad 'versus' los objetos 'Collection' y...
si cualquier duda [o informacion adicional]... comentas ?
saludos,
hector.

en el modulo de codigo del formulario ==Private Sub cmbElegir_Change()
Dim ltC As String
Application.ScreenUpdating = False
If cmbElegir.ListIndex < 0 Then Exit Sub Else ltC = Chr(64 + cmbElegir.ListIndex + 1)
With Worksheets("Listado")
If .AutoFilterMode Then .AutoFilterMode = False
.[ab:ab].Clear
.Columns(ltC).AdvancedFilter xlFilterCopy, , .[ab1], True
.[ab2].Sort Key1:=.[ab2], Order1:=xlAscending, Header:=True
cmbCriterio.List = .Range(.[ab2], .[ab2].End(xlDown)).Value
End With
End Sub

__ los datos y codigos expuestos __
.-Hoja1= "Listado": contiene la lista con los datos. Para las pruebas actuales 5000 registros.
El unico campo que no puede tener registros repetidos es ... columna A) y, a partir de... columna E), puede contener registros vacios.
.-Hoja2= "Matrices": contiene listas para rellenar matrices o variables.
La que interviene en los codigos es la columna A que contiene un listado con todas las letras de la A a la Z
que se corresponden con las columnas de Listado (26 en total)
.-Hoja3= "Oculta": se usa para pegar los datos filtrados y pasarlos al combo de destino.
.-ComboBox1= "cmbElegir": contiene los titulos de los 26 campos de "Listado" (rango "a1:z1"). Se cargan al inicializarse el formulario.
.-ComboBox2= "cmbCriterio": se rellena con los registros no repetidos del campo elegido en "cmbElegir"(cmbElegir_Change).
==>> Este es el codigo con collection:
Private Sub cmbElegir_Change()
Dim rngListado As Range, xList As Long
Dim ListaUnicos As New Collection
Application.ScreenUpdating = False
With cmbElegir
If .Value = .BoundValue Then Exit Sub
cmbCriterio.Clear: lstSeleccionar.Clear
If .ListIndex < 0 Then Exit Sub Else nL = .ListIndex + 1
End With
ltC = Worksheets("Matrices").Cells(nL, 1)
With Worksheets("Listado")
If .AutoFilterMode Then .AutoFilterMode = False
fF = .[a65536].End(xlUp).Row
.Range("a1:z" & fF).Sort _
key1:=.Range(ltC & "1"), _
header:=xlYes
For Each rngListado In .Range(ltC & "2:" & ltC & fF)
On Error Resume Next
If Trim(rngListado) <> "" Then _
ListaUnicos.Add rngListado, CStr(rngListado)
On Error GoTo 0
Next
End With
For xList = 1 To ListaUnicos.Count
cmbCriterio.AddItem ListaUnicos(xList)
Next
Set ListaUnicos = Nothing
Application.ScreenUpdating = True
End Sub

==>> y estos con advanced filter
->1 prueba
Private Sub cmbElegir_Change()
''Se filtra bien, pero mas bien lento _
'''(de 7 a 10 sg con 5000 filas)
Application.ScreenUpdating = False
With cmbElegir
If .Value = .BoundValue Then Exit Sub
cmbCriterio.Clear: lstSeleccionar.Clear
If .ListIndex < 0 Then Exit Sub Else nL = .ListIndex + 1
End With
ltC = Worksheets("Matrices").Cells(nL, 1)
With Worksheets("Oculta")
.UsedRange.EntireRow.Delete
With Worksheets("Listado")
If .AutoFilterMode Then .AutoFilterMode = False
fF = .[a65536].End(xlUp).Row
.Range("a1:z" & fF).Sort _
key1:=.Range(ltC & "1"), _
header:=xlYes
.Range(ltC & "1:" & ltC & fF).AdvancedFilter _
criteriarange:=.Range(ltC & "1:" & ltC & fF), _
Action:=xlFilterCopy, _
copytorange:=Worksheets("Oculta").Range("a1"), _
unique:=True
End With
fFo = .Range("a65536").End(xlUp).Row
If fFo < 2 Then Exit Sub
cmbCriterio.List = .Range("a2:a" & fFo).Value
End With
Application.ScreenUpdating = True
End Sub

->2 prueba (quizas ligeramente mas rapido que el anterior pero no estoy seguro
Private Sub cmbElegir_Change()
Application.ScreenUpdating = False
With cmbElegir
If .Value = .BoundValue Then Exit Sub
cmbCriterio.Clear: lstSeleccionar.Clear
If .ListIndex < 0 Then Exit Sub Else nL = .ListIndex + 1
End With
With Worksheets("Oculta")
.UsedRange.EntireRow.Delete
With Worksheets("Listado")
If .AutoFilterMode Then .AutoFilterMode = False
fF = .[a65536].End(xlUp).Row
.Range("a1:z" & fF).Sort _
key1:=.Cells(1, nL), _
header:=xlYes
.Range(.Cells(1, nL), .Cells(fF, nL)).AdvancedFilter _
criteriarange:=.Range(.Cells(1, nL), .Cells(fF, nL)), _
Action:=xlFilterCopy, _
copytorange:=Worksheets("Oculta").Range("a1"), _
unique:=True
End With
fFo = .Range("a65536").End(xlUp).Row
If fFo < 2 Then Exit Sub
cmbCriterio.List = .Range("a2:a" & fFo).Value
End With
Application.ScreenUpdating = True
End Sub

->3 prueba (como la 1ª pero usando algunas variables mas, tarda igual)
Private Sub cmbElegir_Change()
''Prueba con variables range y variant para ver si agiliza
'''pero sigue mas o menos igual
Dim rngListado As Range, rngFiltrado
Application.ScreenUpdating = False
With cmbElegir
' If .Value = .BoundValue Then Exit Sub
cmbCriterio.Clear: lstSeleccionar.Clear
If .ListIndex < 0 Then Exit Sub Else nL = .ListIndex + 1
End With
ltC = Worksheets("Matrices").Cells(nL, 1)
With Worksheets("Oculta")
.UsedRange.EntireRow.Delete
With Worksheets("Listado")
If .AutoFilterMode Then .AutoFilterMode = False
fF = .[a65536].End(xlUp).Row
.Range("a1:z" & fF).Sort _
key1:=.Range(ltC & "1"), _
header:=xlYes
Set rngListado = .Range(ltC & "1:" & ltC & fF)
rngListado.AdvancedFilter _
criteriarange:=rngListado, _
Action:=xlFilterCopy, _
copytorange:=Worksheets("Oculta").Range("a1"), _
unique:=True
End With
fFo = .Range("a65536").End(xlUp).Row
If fFo < 2 Then Exit Sub
rngFiltrado = .Range("a2:a" & fFo).Value
cmbCriterio.List = rngFiltrado
End With
Application.ScreenUpdating = True
End Sub

Bueno, como siempre me ha salido otro tomo.
Si podeis echarme una mano os lo agradezco.
En cualquier caso, un saludo y hasta pronto.
Ivan
Respuesta Responder a este mensaje
#2 klomkbock
18/07/2006 - 09:59 | Informe spam
Muchas gracias, Hector Miguel

tu respuesta no solo me saca la estaca, sino que me aclara unas cuantas
dudas paralelas(uso de columnas libres-mismas filas- para el rango de
extraccion, sintaxis, ordenacion, ...) y añade algun truco/uso nuevo(chr)
muy interesante.

Aunque las estacas siempre dejan espinas, estas se pueden extraer poquito
a poco, y son mas llevaderas.

Muchas gracias de nuevo

Un saludo y hasta pronto
Ivan

PD: supongo que la lentitud de 'mis' codigos vendria de multiples
factores: ordenacion del rango completo, traspaso a otra hoja, exceso de
variables y de su complejidad.

PPD: cada dia me sorprende mas la 'sencillez' que la logica y el
conocimiento unidos, pueden imprimir a vba.
Respuesta Responder a este mensaje
#3 klomkbock
18/07/2006 - 23:26 | Informe spam
Hola Hector Miguel

Te respondo desde mi primera respuesta por que queria releerla y comprobar
hasta que punto podia haberme precipitado. Una vez releida me reafirmo en
todo lo dicho, a pesar de que tras correr bastantes pruebas, el resultado
siga siendo el mismo:

(para 30000 registros decidi desistir por que se eternizaba (hasta 7
minutos con filtro av.)),

-> reducido el listado a 10000 registros, estos son los resultados hasta ahora
para el campo mas lento:

->con collection =>> 7 segundos aprox.

->con advancedfilter=>> 40 segundos aprox.

¿es posible que se deba a que el listado lo he hecho arrastrando los 70
primeros registros (o sea, repitiendolos) que si eran bastante variados
(nº ficha, titulo, autor, etc), pero aun asi no deja de haber un alto nº
de coincidencias totales?. O quizas tenga que ver con mi pc, aunque en
este caso supongo que tambien deberia afectar a collection.

Tambien he hecho un listado de series
texto-nº ,pej: 'titulo1', 'titulo2', ..., alternando con 'titulo' a
secas y con alguna palabra mas para que no fueran todos registros unicos ,
pero el resultado es el mismo.

He probado a ir anulando instruciones: ordenar o/y cargar el listbox, pero
el resultado no varia. Parece que el lastre esta en el filtrado, pero no
comprendo por que.

Ahora mismo el formulario esta practicamente virgen, solo esta el codigo
que carga el cmbElegir en initialize, y a continuacion el del propio
combo. Y tanto las hojas como thisworkbook no parecen tener nada que pueda
afectar ( de hecho tambien he anulado practicamente todos los codigos).

El codigo usado es el propuesto por ti, pues las diversas pruebas que he
intentado (con variables, etc) no han logrado nada, y lo poco añadido para
prevenir la ausencia de unicos, tambien lo he anulado. En cuanto a la
coleccion es exactamente el expuesto en la consulta.

En cualquier caso me reitero:

la estaca esta sacada, ahora quedan las espinas.

Si puedes seguir echandome una mano te lo agradezco y de todas formas
gracias por todo lo anterior.

Un saludo y hasta pronto
Ivan
Respuesta Responder a este mensaje
#4 Héctor Miguel
19/07/2006 - 07:52 | Informe spam
hola, Ivan !

1) me he 'construido' un libro con ~ 1000 'registros' en las columnas 'A a la Z' [+/- 260000 'datos'] <= NO FORMULAS <= OJO
2) [obviamente] agregue un formulario con los combos y algunos labels para 'informar' los tiempos 'consumidos' [en milisegundos]
3) el 'funcionamiento' del llenado de los combos [tanto por colecciones como filtros-avanzados] es +/- el plasmado en las consultas
-> los resultados son como sigue:
a) filtros avanzados... -> 140 milisegundos
b) objetos collection...-> 680 milisegundos
-> en general/promedio/... los filtros avanzados resultan +/- 5 veces mas rapdios que los objetos collection ;)

4) aun las pruebas que realice con los ~ 30000 registros... [estoy seguro que] no podria 'alcanzar' los 7 MINUTOS que comentas -?-

5) [me temo que] no tengo ni siquiera la minima idea de la/s -posible/s- causas del tiempo te 'reportan' ambos procedimientos :-(
[sobre todo, que 'te' resulte mas lento usar filtros-avanzados que los objetos collection] -?-

si te sirve como 'punto de comparacion' [versus otros codigos/eventos/circunstancias/eventualidades/... con tu situacion 'real']...
te puedo enviar el archivo 'probado' [son 4 Mb y 1.2 Mb comprimido] :))

si cualquier duda [o informacion adicional]... comentas ?
saludos,
hector.

__ -recorte de- la consulta original __
... desde mi primera respuesta por que queria releerla y comprobar hasta que punto podia haberme precipitado.
... me reafirmo en todo lo dicho, a pesar de que tras correr bastantes pruebas, el resultado siga siendo el mismo:
(para 30000 registros decidi desistir por que se eternizaba (hasta 7 minutos con filtro av.)),
-> reducido el listado a 10000 registros, estos son los resultados hasta ahora para el campo mas lento:
-> con collection =>> 7 segundos aprox.
-> con advancedfilter =>> 40 segundos aprox.
es posible que se deba a que el listado lo he hecho arrastrando los 70 primeros registros (o sea, repitiendolos)
que si eran bastante variados (nº ficha, titulo, autor, etc), pero aun asi no deja de haber un alto nº de coincidencias totales?.
O quizas tenga que ver con mi pc, aunque en este caso supongo que tambien deberia afectar a collection [...]
Respuesta Responder a este mensaje
#5 Héctor Miguel
19/07/2006 - 08:04 | Informe spam
hola [de nuevo], Ivan ! [fe de erratas]

1) me he 'construido' un libro con ~ 1000 'registros' en las columnas 'A a la Z' [+/- 260000 'datos'] <= NO FORMULAS <= OJO



son ~ 10000 [diez mil] registros... no ~ 1000 [mil] ;)

saludos,
hector.
Respuesta Responder a este mensaje
Ads by Google
Help Hacer una preguntaSiguiente Respuesta Tengo una respuesta
Search Busqueda sugerida