I need help with formula structure,
I have 2 tables. I would like to find a match where column a & column b equal and get the address in table 2. They will be unique entries. So for example:
ProjectInfoTable :
A | B | C |
------------------------------------------
1 | Name | Company | Project |
------------------------------------------
2 | Chris Evans | Apple | Info |
------------------------------------------
3 | Chris Evans | Google | Info |
------------------------------------------
4 | Bill Gates | Burger King | Info |
------------------------------------------
ClientInfoTable:
A | B | C | D
-------------------------------------------
1 | Client | Company | Age | Sex |
-------------------------------------------
2 | Chris Evans | Apple | 12 | M |
-------------------------------------------
3 | Chris Evans | Google | 17 | M |
-------------------------------------------
4 | Bill Gates | Burger King | 98 | F |
-------------------------------------------
I would like to be able to, while in 'ProjectInfoTable', get the address of the matching Name & Company client in 'ClientInfoTable'
The trouble I'm running into is there could be a thousand different Chris Evans out there, so VLOOKUP isn't good here. I need to make sure it's the same Chris Evans in 'ClientInfoTable' that I'm looking at in 'ProjectInfoTable' by cross-referencing their companies
I can get the address no problem if I only search it by name:
=ADDRESS(ROW(INDEX(ClientInfoTable,MATCH([@[Client]],ClientInfoTable[Client],0),1)),COLUMN(INDEX(ClientInfoTable,MATCH([@[Client]],ClientInfoTable[Client],0),1)),1,1,"Clients")
but I need to add an additional condition of their companies so now that formula is useless.
Any ideas out there? I don't want to use hidden columns, or 'helper columns'
I will except VBA or formula based answers. I'll even reward a bounty to anyone who can provide both, assuming that the data will always be dynamic ranges, and then gives a good explanation of your code/formula. I am here to learn, I am not a copy/paste type of user, explanations go a long with me.
You can also use the function I wrote.Put it to any module in the file where you want to use:
Example of usage:
In case of error it returns -1; otherwise it returns number of row where it matches.
If you want the full address, you can use this function together with ADDRESS and MATCH functions:
You can combine VLookUp with Choose to have more than one criteria. In your example:
This is a matrix formula so you have to close it with Ctrl+Shift+Enter.
What happens is the following: the choose-function chooses the first and second value that it is given (that's what the first argument {1.2} says). As this is a matrix formula you get not only one pair of values but one for each row. In this example you get a matrix:
This is the matrix you can now use for VLookUp.
You should make sure that the delimiter (comma in this example) does not occur in your data.
If you only need the address and not the actual values, use the function Match instead of vlookup:
This returns the row the corresponding set is in within the given matrix. If the matrix does not begin in row 1 you have to add the matrix-position.
One way to approach your problem is to use the SQL support in Excel VBA via ADODB. This Microsoft article explains how to do this.
Using the SQL support, you basically have two options: either you write a functions in VBA that returns the (first) value for a given name and company from the
ClientInfoTable
, using aSELECT
statement, or you write a sub in VBA that directly inserts the values for all row inProjectInfoTable
wherever you need them, using a join between theProjectInfoTable
and theClientInfoTable
on the name and the company.Formula:
Here's a formula only solution with no hidden/ helper columns and no array formulas:
Components:
a
--INDEX(ClientInfo[Client]=$A5,0)
- returns a boolean array for as many matches there are for e.g.Chris Evans
inClientInfo[Client]
. In the example below this will be{TRUE,TRUE,FALSE,FALSE}
. This is then cast to array of integer with the double unary operator to leave{1,1,0,0}
b
--INDEX(ClientInfo[Company]=$B5,0)
- same thing as a e.g.Apple
inClientInfo[Company]
in the example is the array{TRUE,FALSE,FALSE,TRUE}
- which is then cast to{1,0,0,1}
c
INDEX(a*b,0)
- multiples elements 1..n of array a with elements 1..n of array b. In our example this results in{1,0,0,0}
and at this point you have identified your matching composite key for Chris Evans and Apple as being the 1st row of ClientInfod
MATCH(1,c,0)
- gets the index of the 1 in the array which in our example of Chris Evans and Apple is 1. You mentioned They will be unique entries so I don't think we have a problem here.e
ROW(ClientInfo)-1+d
- I definedClientInfo
as a Table/ ListObject with a range ofA8:D12
but the reference is giving backA9:D12
which seems to be the way it works the named ranges for Tables/ ListObjects. So we need to deduct one from theROW
of that range to get the start of the offset; then simply add the result of d.f
ADDRESS(e,COLUMN(ClientInfo))
- returns the cell address of e and the first column ofClientInfo
table.Example:
VBA:
Using the example above, the VBA method will do the following:
The code:
PS I was hesitant to provide the VBA answer as well as you already accepted a decent answer. However, I have a slight preference for doing this without updating the UI although I agree the
AutoFilter
method is good enough. HTHCreate a VBA function that takes the clientname and company as string arguments - then loops through the clients table and return the address if found. Below code will return "Not found#" if there is no match
To use this function import the code into a macro workbook as a VBA module (remember to enable macros). Then type the formula into an cell like a normal excel formula
or
you could use
AutoFilter()
: