Google Sheets: Merge two tables (arrays) with many to many relationship - google-sheets

I have 2 tables (arrays) in Google Sheets, that they have a related column. And I need to join these tables.
What I need is get a result being that the two tables have a many-to-many relationship.
Example sheet
In this example, I have two tables (A:C and E:G). I need a result like table I:N.
Table A:C -> I have players and how much them has completed certain game. (key: 'player'+'game')
Table E:G -> I have groups that each one has different goals for some games. (key: 'group'+'game')
I need a result that column 'game' is the related column of each table.
Note that:
row 15 of first table: (d - b6 - 55%), and
row 7 of second table: (k7 - b7 - 60%),
have no relation, so they aren't on third table.
I've tried to combine some formulas like ARRAYFORMULA, VLOOKUP, FILTER, INDEX, OFFSET...
But I hadn't success with none of them.
I've found a solution with script SUPERSQL() Function, but I wouldn't like to use scripts, just formulas.

Main idea is to place one key column vertically as it is, and another one horizontally (TRANSPOSE()).
Outside QUERY is just for rearranging columns and sorting.
More on TRANSPOSE(QUERY(TRANSPOSE(...))) and such is here.
Solution:
=QUERY(
{
A1:C1\ E1:G1;
ARRAYFORMULA(
SPLIT(
REGEXREPLACE(
QUERY(
FLATTEN(
IF(
FILTER(B2:B; B2:B <> "") <> TRANSPOSE(FILTER(F2:F; F2:F <> ""));;
TRANSPOSE(QUERY(TRANSPOSE(FILTER(A2:C & "#"; B2:B <> ""));; COLUMNS(A2:C))) & " "
& QUERY(TRANSPOSE(FILTER(E2:G & "#"; F2:F <> ""));; COLUMNS(E2:G))
)
);
"WHERE Col1 IS NOT NULL";
);
"#$";
);
"# ";
)
)
};
"SELECT Col4, Col1, Col2, Col6, Col3
ORDER BY Col4, Col1, Col2";
1
)

Related

How to append 3 tables with different number of columns in google sheet?

Hi everyone,
I have 3 tables with different number of columns where some columns are actually repeated in these 3 tables (as shown in the screenshot above).
Table 1: Col A to Col D
Table 2: Col F to Col H
Table 3: Col J to Col L
I want to merge these 3 tables together to reduce the number of columns in my google sheet. The screenshot below is how I process these 3 tables before merging.
I'm using QUERY to create extra columns for each table so that the number of columns and the position are aligned between 3 tables. After that, I use QUERY again to append the 3 processed tables as shown in the screenshot below:
However, this method is very tedious when I have 10 tables or more. Is there any other easier ways or tricks to use so that I can achieve the same expected output as shown in the 3rd screenshot?
This is my sheet:
https://docs.google.com/spreadsheets/d/1H1gJAhp1RVax2fy8D-uEtFxdjb-zAHutkPFv5WZT_TY/edit#gid=0
Any help will be greatly appreciated!
You would need a really complicated formula to get the desired output which is a combination of multiple formula's
I added a new tab in you Google Sheet called "Solution" with this formula included
=QUERY(ARRAYFORMULA({
IFERROR( ArrayFormula(SPLIT(FLATTEN(Sheet1!B3:D3&"~"&Sheet1!A4:A&"~"&Sheet1!B4:D),"~")),"");
IFERROR( ArrayFormula(SPLIT(FLATTEN(Sheet1!G3:H3&"~"&Sheet1!F4:F&"~"&Sheet1!G4:H),"~")),"");
IFERROR( ArrayFormula(SPLIT(FLATTEN(Sheet1!K3:L3&"~"&Sheet1!J4:J&"~"&Sheet1!K4:L),"~")),"")}),
"SELECT Col2, SUM(Col3) WHERE Col2 is not null GROUP BY Col2 PIVOT Col1 LABEL Col2 'Student Name' ")
Steps:
Unpivot each table =IFERROR( ArrayFormula(SPLIT(FLATTEN(Sheet1!B3:D3&"~"&Sheet1!A4:A6&"~"&Sheet1!B4:D6),"~")),"")
Combine all tables into one table: =ARRAYFORMULA({__Unpivot1__; __Unpivot2__, __Unpivot3__})
Pivot above data in Step2: =Query(__Step2__, "SELECT Col2, SUM(Col3) GROUP BY Col2 PIVOT Col1 LABEL Col2 'Student Name' ")
Add WHERE Col2 is not null in the query to remove any possible blank rows

Match over a concatenation of a whole column?

I want to collect only the rows which are different between two tables.
The PK are the first two columns.
How can I fix this formula? I guess I use V:W incorrectly.
=if(Q11&" "&R11="","", isnumber(match(Q11&" "&R11, V:W,0)))
Let's say that you have 'today' data in column B
And 'last time' data in column C
And you want to get values that exist only in 'today' column but not in 'last time'.
You can use:
=query(
ArrayFormula(
if(countif(C2:C,B2:B)=0,
B2:B,
""
)),
"select Col1 where Col1 <> ''"
)
As you use more columns in your datatable, you can concatenate and use for example: C2:C&" "&D2:D&" "&E2:E instead of one column.

Google sheets query multiple rows+cols to one cell with lookup table

Cannot figure out how to pull the results of a query with multiple rows and columns into single cells and find their details from my lookup table. Any help is greatly appreciated.
Trying this
=ArrayFormula(
QUERY({
QUERY({A4:A16,TEXTJOIN(",",TRUE,B4:C16)}, "SELECT Col1, Col2, Col3 WHERE Col1 MATCHES 'Core 1|Core 2|Core 3' LABEL Col1 'Core ID', Col2 'Full Result'")
}, "SELECT Col2 WHERE Col1 = Col1")
)
Sheet to help
It's always a headache trying to mix Query with ArrayFormula. They don't go well together.
I arrived at a nice formula, but the catch is that it relies on you sorting your data by Core Item first. You can easily achieve this with:
=SORT(A4:C16)
For this example, I placed this formula is cell N4. Then your resultant formula is:
=ArrayFormula(
{
"Core ID", "Full Result";
UNIQUE(FILTER(N4:N,N4:N<>"")),
SUBSTITUTE(
TRANSPOSE(
SPLIT(
REGEXREPLACE(
TEXTJOIN(
",",
,
UNIQUE(
TRANSPOSE(
IF(
TRANSPOSE(FILTER(N4:N,N4:N<>""))=FILTER(N4:N,N4:N<>""),
FILTER(O4:O,O4:O<>"")&
" ("&
VLOOKUP(FILTER(O3:O,O3:O<>""),A23:B53,2,)&
") - "&
FILTER(P4:P,P4:P<>"")&
" ("&
VLOOKUP(FILTER(P4:P,P4:P<>""),A23:B53,2,)&
")",
";"
)
)
)
),
",(;,)+",
";"
),";"
)
),
",",
CHAR(10)
)
})
It uses the Group Concatenate method I've adopted for stuff like this with UNIQUE, TRANSPOSE, and IF. This can probably be solved without using the precalculated SORT by substituting all instances of the N4:N, but doing it this way saves on computation time. If you need it gone, you can also hide the column or stow it in another sheet.
If you are ok breaking apart your formula then this would be an acceptable approach.
Start by Building your query criteria.
=ArrayFormula(
QUERY({QUERY({A4:A16,ArrayFormula(B4:B16&" ("&VLOOKUP(B4:B16,A23:B53,2,false)&") -
("&VLOOKUP(C4:C16,A23:B53,2,false)&")")}, "SELECT Col1, Col2 WHERE
Col1 MATCHES 'Core 1|Core 2|Core 3' LABEL Col1 'Core ID', Col2 'ALL Result'")},
"SELECT Col1, Col2 WHERE Col1 = Col1"))
Then in another column get the unique values and lastly, join the filtered values.
=UNIQUE(E4:E17)
=Join(Char(10),FILTER(F4:F12,E4:E12 = H4))
The downside to this is you will need to have multiple columns to achieve your desired result but it is easy to follow. Good luck!

Formula to fill a two dimensional grid with computed value from a table

Here is a problem to solve with a Google Sheets formula.
I have a big table (sheet “data” with headers on the first row) with those columns:
A, product reference
B: customer
C to F, KPI1 to KPI4
On another sheet, a grid of product references (A2:A) by customer (B1:1).
Now I need to fill each cell from the grid with the concatenation of KPIs (data!C24&"|"&data!D24&"|"&data!E24&"|"&data!F24)
Could you workout a single formula to fill all the cells?
Here is a sample spreadsheet with the data and grid sheet:
https://docs.google.com/spreadsheets/d/1iA_kw4kKw99Qk69X4tST9U-QN2SeG2EN3KEeyG6AtHs/edit?usp=sharing
I have worked out a formula which does the job, though with very poor performance on large dataset:
=ARRAYFORMULA(
IFNA(
VLOOKUP(
$B3:$B&"|"&C$2:$2,
ARRAYFORMULA(
{data!A2:A&"|"&data!B2:B,data!C2:C&"|"&data!D2:D&"|"&data!E2:E&"|"&data!F2:F}
),2,0
),""
)
)
Solution
Use an ArrayFormula on a Query with Pivot:
=ARRAYFORMULA(
QUERY(
{data!A2:A, data!B2:B, data!C2:C&"|"&data!D2:D&"|"&data!E2:E&"|"&data!F2:F},
"select Col1,max(Col3) where Col1 is not null group by Col1 pivot Col2",0
)
)
a shorter version of previous answer (no need for pre-sorting coz pivot will sort it on its own):
=ARRAYFORMULA(QUERY(
{data!A2:A, data!B2:B, data!C2:C&"|"&data!D2:D&"|"&data!E2:E&"|"&data!F2:F},
"select Col1,max(Col3) where Col1 is not null group by Col1 pivot Col2", 0))
Try this on the first cell of your grid:
=ArrayFormula(query(sort({data!A:A,data!B:B,transpose(substitute(query(transpose(data!C:F),,4)," ","|"))},1,true,2,true),"select Col1, max(Col3) where Col1 is not null group by Col1 pivot Col2",0))

2 spreadsheets, IMPORTRANGE update references when adding/deleting rows

How can I get the IMPORTRANGE function to update references in Spreadsheet #2 if I've added/deleted rows to Spreadsheet #1?
Test docs:
Spreadsheet1: "S1"
Spreadsheet2: "S2"
All the answers I've found say
just duplicate/copy sheet from S1 to S2 (but I want S2 to dynamically update when I update S1. don't want to update both S1 and S2 each time)
Use this script, but I got an error saying the range was wrong
use an array (not sure how to do that...)
use address feature (but this did not work either)
use VLOOKUP array (but my spreadsheets do not have the matching column headers)
tried using absolute reference $L$48 but this didnt work either
Would appreciate some assistance figuring this out. I need L47, M47 (S1) to populate into A2, A3 (S2). My actual source spreadsheet (not linked) has tons of data (range A1:Z55) and I add/delete/move rows often.
If I add a row to S1, how can I ensure L47 will update in S2 to L48? Is the address feature the best solution or is an array better?
this can be achieved by adding an extra column far away, where it can be hidden and then populating this column by joining desired set of cells by unique separator until split will occur on the second spreadsheet. note that:
adding or deleting rows will not affect dynamicity of IMPORTRANGE
adding deleting columns will break all imported data
there is no need for an extra column if there is a unique separator per every IMPORTRANGE of data and the search is applied always to such unique separator
in this particular case, there was used column AG from which IMPORTRANGE was fed.
in Spreadsheet1 in Sheet1!AG (no matter of row number) there are formulas which JOIN content of L50 and M50 as well as the content of L51 and M51, etc... (no matter if it's done directly or indirectly as far as the output is TEXT):
=JOIN("¤"; L50; MIN(FILTER(L:L; ISNUMBER(SEARCH("*banana*"; P:P))
+ISNUMBER(SEARCH("*banana*"; Q:Q))
+ISNUMBER(SEARCH("*banana*"; R:R)))))
outputing: next banana¤30-Aug-2004
=JOIN("¤"; L51; MIN(FILTER(L:L; ISNUMBER(SEARCH("*orange*"; P:P))
+ISNUMBER(SEARCH("*orange*"; Q:Q))
+ISNUMBER(SEARCH("*orange*"; R:R)))))
outputing: next orange¤2-Oct-2003
=JOIN("♥"; L52; AVERAGE(FILTER(L:L; ISNUMBER(SEARCH("orange"; P:P))
+ISNUMBER(SEARCH("orange"; Q:Q))
+ISNUMBER(SEARCH("orange"; R:R)))))
outputing: X♥25-Sep-2013
=JOIN("♀"; L53; MIN(FILTER(L5:L48; ISNUMBER(SEARCH("*banana*"; Q5:Q48))
*ISNUMBER(SEARCH("open"; R5:R48)))))
outputing: next banana♀20-Aug-2000
=JOIN("♂"; L54; AVERAGEIFS(M5:M48; R5:R48; "open",
Q5:Q48; "*banana*"))
outputing: avg days open (banana)♂74.41
=JOIN("♪"; L55; Q50/Q51)
outputing: util♪0.370544987
=JOIN("♫"; L56; MINIFS(M5:M48; R5:R48; "open",
Q5:Q48; "*banana*"))
outputing: newest (mo)♫3.48
=JOIN("¤"; L57; M56*30.5)
outputing: newest(days)¤106.2580645
=JOIN("♤"; L58; M58)
outputing: avg LMT♤25051.35484
at this point, it doesn't matter if the format of joined cells is outputting elsehow (eg. 2nd part of the output should be formatted as $, %, mm/dd/yyyy) because in Spreadsheet2 after splitting you can format it back as you wish
in Spreadsheet2 you are free to paste following formula at any column and any row as well as you are free to:
add or delete any rows in Spreadsheet1
and add or delete any rows or columns in Spreadsheet2
=SPLIT(
ARRAY_CONSTRAIN(
QUERY(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
"select Col1 where Col1 ='"&
FILTER(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
ISNUMBER(
SEARCH("banana";
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000"))
))
&"'");
1; 1);
"¤"; 1; 0)
this basically SEARCHes for text value "banana" in Spreadsheet1 under Sheet1 from range AG1:AG1000 and feed it to the FILTER which feeds criterion of QUERY which is ARRAY_CONSTRAINed to return one entry and that entry is SPLIT after unique separator "¤" (used earlier in JOIN) into two columns at the same row. and that's it.
if the content of cell L50 is static like banana and also unique per column you can SEARCH for "banana" otherwise you need to use unique separator per column and SEARCH for such separator instead of "banana"
for a successful linkup, you need to be sure that separator in SPLIT matches separator in JOIN ("¤"). you can use any symbol you wish as the separator (http://www.i2symbol.com/symbols)
example: for formula =JOIN("♤"; L58; M58) you can use:
=SPLIT(
ARRAY_CONSTRAIN(
QUERY(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
"select Col1 where Col1 ='"&
FILTER(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
ISNUMBER(
SEARCH("lmt";
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000"))
))
&"'");
1; 1);
"♤"; 1; 0)
or
=SPLIT(
ARRAY_CONSTRAIN(
QUERY(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
"select Col1 where Col1 ='"&
FILTER(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
ISNUMBER(
SEARCH("♤";
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000"))
))
&"'");
1; 1);
"♤"; 1; 0)
or
=SPLIT(
ARRAY_CONSTRAIN(
QUERY(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
"select Col1 where Col1 ='"&
FILTER(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
ISNUMBER(
SEARCH("avg LMT";
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000"))
))
&"'");
1; 1);
"♤"; 1; 0)

Resources