例えば、A列とB列のデータを比較して、「A列にだけあるデータ」「B列にだけあるデータ」「A列とB列の両方にあるデータ」を抜き出したい時、どんな方法を思い浮かべるでしょうか。色んな方法がありますが、今回は数式でやる方法をまとめてみました。
後半では関数化する方法や、まとめて表示する方法についても触れています。

一応最初に書いておきますが、今回のデータ(A・B)は「それぞれの範囲内での重複は省いてある」ものとします。
COUNTIF関数を使う方法
おそらく真っ先に思い浮かぶ方法ではないかと思います。というのも「『範囲1』にあって『範囲2』にないデータの抽出」は単純には、
=FILTER(範囲1,COUNTIF(範囲2,範囲1)=0)
これで済むからです。
とはいえこれだけでは色々問題が起きます。「範囲1」を列範囲で指定してしまうと、猛烈に計算が重くなりますし、「範囲1」と「範囲2」のどちらかに全くデータがない時、この数式のままでは不具合が起きます。そこで少し数式を補強します。
「範囲1」にあって「範囲2」にないデータを抽出(COUNTIF関数の場合)
=LAMBDA(範囲1,範囲2,
IFERROR(IF(ISERR(@範囲2),範囲1,FILTER(範囲1,COUNTIF(範囲2,範囲1)=0)),"")
)(DROP(A:.A,1),DROP(B:.B,1))
TRIM参照(「:.」とすることで不要な下位行を省く)とDROP関数(第二引数で指定した行数だけ上位行を削除する)を使うことで、純粋なデータ範囲のみを指定できます。また「ISERR(@範囲2)」で「範囲2」のデータがない場合のエラーをチェックして、条件分岐しています。
TRIM参照は今のところ、Microsoft365限定なので、Excel2024の場合は
=LAMBDA(範囲1,範囲2,
IFERROR(IF(ISERR(@範囲2),範囲1,FILTER(範囲1,COUNTIF(範囲2,範囲1)=0)),"")
)(DROP(TOCOL(A:A,1),1),B:B)
のようにしてください。実のところ、COUNTIF関数は最終行を取得してから計算する関数なので、「範囲2」のほうは列参照のままでも問題ありません。
「『範囲2』にあって『範囲1』にないデータの抽出」は、それぞれの参照を逆にすればいいですし、「両方の範囲にあるデータの抽出」は「=0」のところを「<>0」もしくは「>0」とすればいいです。

「範囲2」にあって「範囲1」にないデータを抽出(COUNTIF関数の場合)
=LAMBDA(範囲1,範囲2,
IFERROR(IF(ISERR(@範囲1),範囲2,FILTER(範囲2,COUNTIF(範囲1,範囲2)=0)),"")
)(DROP(A:.A,1),DROP(B:.B,1))
「範囲1」にも「範囲2」にもあるデータを抽出(COUNTIF関数の場合)
=LAMBDA(範囲1,範囲2,
IFERROR(IF(ISERR(@範囲2),範囲1,FILTER(範囲1,COUNTIF(範囲2,範囲1)>0)),"")
)(DROP(A:.A,1),DROP(B:.B,1))
UNIQUE関数を使う方法
COUNTIF関数の替わりにUNIQUE関数を使う方法もあります。COUNTIF(もしくはCOUNTIFS)関数は、第一引数にセル範囲しか入れることができません。配列を入れられないので、応用したい時に不便な場合があります。
UNIQUEにはそのような制限がありません(ただしVSTACK関数を使っているので、Excel2021以前のバージョンでは使えません)。
「範囲1」にあって「範囲2」にないデータを抽出(UNIQUE関数の場合)
=LAMBDA(範囲1,範囲2,
IFERROR(IF(ISERR(@範囲2),範囲1,UNIQUE(VSTACK(範囲1,範囲2,範囲2),,TRUE)),"")
)(DROP(A:.A,1),DROP(B:.B,1))
UNIQUE関数は第3引数を「TRUE」にすることで、「重複していない値」のみを表示します。COUNTIF関数と違って、第1引数にセル範囲だけでなく配列を入れることもできます。そこで、VSTACKで「範囲1」と「範囲2」を縦結合して、その配列の中から「重複していない値」を抽出しているわけです。
「範囲2」にあって「範囲1」にないデータを抽出(UNIQUE関数の場合)
=LAMBDA(範囲1,範囲2,
IFERROR(IF(ISERR(@範囲1),範囲2,UNIQUE(VSTACK(範囲2,範囲1,範囲1),,TRUE)),"")
)(DROP(A:.A,1),DROP(B:.B,1))
「範囲1」にも「範囲2」にもあるデータを抽出(UNIQUE関数の場合)
=LAMBDA(範囲1,範囲A,
IFERROR(UNIQUE(VSTACK(範囲1,範囲A,範囲A),,TRUE),"")
)(DROP(A:.A,1),D2#)
この方法を応用することで「重複している値」だけを表示させることもできます。
抽出条件を切り替えできる関数を作る
注意:ここから先は応用です。別に読まなくても問題ありません。
ここまでの数式をよく見てみると、どの数式もそれほど違いがないことに気づくのではないでしょうか。「こことここ以外は同じ」と分かったらしめたもので、その「ここ」を変数にして関数化してしまいましょう。

抽出条件を指定して切り替える関数(COUNTIF関数の場合)
=LAMBDA(範囲1,範囲2,指定,LET(
f,LAMBDA(a,b,c,d,IFERROR(IF(ISERR(@b),c,FILTER(a,COUNTIF(b,a)+d=1)),"")),
Aのみ,f(範囲1,範囲2,範囲1,1),
Bのみ,f(範囲2,範囲1,範囲2,1),
ABとも,f(範囲1,範囲2,"",0),
CHOOSE(指定,Aのみ,Bのみ,ABとも)
))(DROP(A:.A,1),DROP(B:.B,1),★)
「★」に「1」~「3」を入力することで、抽出条件が「Aのみ」「Bのみ」「ABとも」と切り替わります。同様にUNIQUE関数でもやってみます。
抽出条件を指定して切り替える関数(COUNTIF関数の場合)
=LAMBDA(範囲1,範囲2,指定,LET(
f,LAMBDA(a,b,c,IFERROR(IF(ISERR(@b),a,UNIQUE(VSTACK(a,b,b),,TRUE)),"")),
Aのみ,f(範囲1,範囲2,範囲1),
Bのみ,f(範囲2,範囲1,範囲2),
ABとも,f(範囲1,Aのみ,""),
CHOOSE(指定,Aのみ,Bのみ,ABとも)
))(DROP(A:.A,1),DROP(B:.B,1),★)
これも「★(1~3)」で抽出条件が「Aのみ」「Bのみ」「ABとも」と切り替わっていきます。
条件指定に配列を使えるようにする
ここから更に応用してみます。先ほどの数式の「★」を複数列指定できるようにして、一度に表示できるようにしてみましょう。

=LAMBDA(範囲1,範囲2,[指定],LET(
f,LAMBDA(a,b,c,d,IFERROR(IF(ISERR(TAKE(b,1)),c,FILTER(a,COUNTIF(b,a)+d=1)),"")),
Aのみ,f(範囲1,範囲2,範囲1,TRUE),
Bのみ,f(範囲2,範囲1,範囲2,TRUE),
ABとも,f(範囲1,範囲2,"",FALSE),
列選択,IF(ISOMITTED(指定),{1,2,3},指定),
データ,IFERROR(CHOOSECOLS(HSTACK(Aのみ,Bのみ,ABとも),列選択),""),
IFERROR(FILTER(データ,BYROW(データ,LAMBDA(x,OR(x<>"")))),"")
))(DROP(A:.A,1),DROP(B:.B,1),{1,2,3})
これで「1」のように指定することもできますし、表示させる条件列を「{1,2}」のように配列で指定することもできます。どうせなので、UNIQUE関数のほうでもやってみます。
=LAMBDA(範囲1,範囲2,[指定],LET(
f,LAMBDA(d,[tf],IFERROR(UNIQUE(d,,IF(ISOMITTED(tf),TRUE,tf)),"")),
a,f(範囲1,FALSE),b,f(範囲2,FALSE),
結合,VSTACK(a,b),重複,f(結合),
Aのみ,f(VSTACK(結合,b)),Bのみ,f(VSTACK(結合,a)),
ABとも,f(VSTACK(a,重複,重複)),
列選択,IF(ISOMITTED(指定),{1,2,3},指定),
データ,IFERROR(CHOOSECOLS(HSTACK(Aのみ,Bのみ,ABとも),列選択),""),
IFERROR(FILTER(データ,BYROW(データ,LAMBDA(x,OR(x<>"")))),"")
))(DROP(A:.A,1),DROP(B:.B,1))
因みに第3引数を省略した場合は、3条件を全て表示させる仕様になっています。
「こんな長々とした式、使いづらいじゃないか」と思うかもしれませんが、LAMBDA関数の式は名前定義しておくことで、そのブック上では関数として使用できます。今回の数式を「INTERSECT」のような名前で関数として定義しておけば、実際のセル上では、
=INTERSECT(DROP(A:.A,1),DROP(B:.B,1))
とするだけでよくなります。