web-dev-qa-db-fra.com

VBA (Excel): recherche basée sur plusieurs critères de recherche sans boucle

J'ai une grande fiche technique que je souhaite rechercher dans VBA sur la base de 3 ensembles de critères . Chaque entrée de ligne peut être considérée comme unique. Le format de la feuille/des données elles-mêmes ne peut pas être modifié en raison des exigences. (J'ai vu plusieurs articles sur des questions connexes, mais je n'ai pas encore trouvé de solution de travail pour cela.)

Au début, j'ai utilisé la méthode classique VBA find en boucle:

Set foundItem = itemRange.Find(What:=itemName, Lookin:=xlValues, lookat:=xlWhole, SearchOrder:=xlByRows)
If Not foundItem Is Nothing Then
    firstMatchAddr = foundItem.Address
    Do
        ' *Check the other fields in this row for a match and exit if found*
        Set foundItem = itemRange.FindNext(foundItem)
    Loop While foundItem.Address <> firstMatchAddr  And Not foundItem Is Nothing
End If

Mais comme cela doit être appelé un certain nombre de fois sur de grands ensembles de données, la vitesse n'était pas bonne.

J'ai fait quelques recherches et j'ai découvert que je pouvais utiliser la méthode match avec index . J'ai donc essayé sans succès de nombreuses variantes de cela, telles que:

result = Evaluate("=MATCH(1, (""" & criteria1Name & """=A2:A" & lastRow & ")*(""" & criteria2Name & """=B2:B" & lastRow & ")*(""" & criteria3Name & """=C2:C" & lastRow & "), 0)")

Et

result = Application.WorksheetFunction.Index(resultRange, Application.WorksheetFunction.Match((criteria1Name = criteria1Range)*(criteria2Name = criteria2Range)*(criteria3Name = criteria3Range))

Et

result = Application.WorksheetFunction.Index(resultRange, Application.WorksheetFunction.Match((criteria1Range=criteria1Name )*(criteria2Range=criteria2Name )*(criteria3Range=criteria3Name ))

Ensuite, j'ai essayé d'utiliser AutoFilter pour trier:

.Range(.Cells(1,1), .Cells(lastRow, lastCol)).AutoFilter Field:=1, Criteria1:="=" & criteria1Name
.Range(.Cells(1,1), .Cells(lastRow, lastCol)).AutoFilter Field:=2, Criteria1:="=" & criteria2Name
.Range(.Cells(1,1), .Cells(lastRow, lastCol)).AutoFilter Field:=3, Criteria1:="=" & criteria3Name

Mais comme l'une des colonnes de tri contient des dates, j'ai eu des problèmes pour que le filtre automatique fonctionne correctement.

Ma question est, comment puis-je rechercher dans les colonnes dans Excel VBA en fonction de plusieurs critères, sans boucle, en retournant le numéro de ligne ou la valeur dans la cellule de cette ligne qui m'intéresse ?

7
user1205577

Vous pouvez utiliser un filtre avancé. Placez les en-têtes de colonne dans une partie distincte de la feuille (ou une feuille complètement différente). Sous ces en-têtes de colonne, placez les critères que vous recherchez dans chaque colonne. Nommez ensuite cette plage (y compris les en-têtes) quelque chose comme "Critères". La macro devient alors:

Sub Macro1()

    Sheets("Sheet1").Range("A1").CurrentRegion.AdvancedFilter xlFilterInPlace, Range("Criteria")

End Sub

Pour faire suite à mon commentaire ci-dessous, pour que le VBA crée la plage de critères dans les coulisses:

Sub Macro1()

    'Code up here that defines the criteria

    Application.ScreenUpdating = False
    Application.DisplayAlerts = False

    With Sheets.Add
        'Create the advanced filter criteria range
        .Range("A1") = "HeaderA"
        .Range("B1") = "HeaderB"
        .Range("C1") = "HeaderC"
        .Range("A2") = criteria1Name
        .Range("B2") = criteria2Name
        .Range("C2") = criteria3Name

        'Alternately, to save space:
        '.Range("A1:C1").Value = Array("HeaderA", "HeaderB", "HeaderC")
        '.Range("A2:C2").Value = Array(criteria1Name, criteria2Name, criteria3Name)

        'Then perform the advanced filter
        Sheets("Sheet1").Range("A1").CurrentRegion.AdvancedFilter xlFilterInPlace, .Range("A1:C2")

        'Remove behind the scenes sheet now that the filter is completed
        .Delete
    End With

    Application.ScreenUpdating = True
    Application.DisplayAlerts = True

End Sub
7
tigeravatar

Vous pouvez utiliser EVALUATE pour plusieurs critères comme pour renvoyer les numéros de ligne des valeurs mathématiques. Cela utilise la même approche que Est-il possible de remplir un tableau avec des numéros de ligne qui correspondent à certains critères sans boucle?

  • Il recherche 50000 lignes pour correspondre à
    • les quatre premières lettres de la colonne A correspondent à fred
    • la date en B doit être supérieure à 1/1/2001
    • Apple dans la colonne 5
  • Toutes les lignes correspondant à ces critères sont réaccordées en tant que numéros de ligne dans x

(lignes 1 et 5 dans l'image ci-dessous)

code

Sub GetEm2()
x = Filter(Application.Transpose(Application.Evaluate("=IF((LEFT(A1:A10000,4)=""fred"")*(B1:B10000>date(2001,1,1))*(C1:C10000=""Apple""),ROW(A1:A10000),""x"")")), "x", False)
End Sub

Application.Transpose est limité à 65536 cellules, donc une plage plus longue doit être "fragmentée" en morceaux.

enter image description here

5
brettdj