CHOOSE 和 INDEX:不写 IF 也能分支

遇到“根据不同情况返回不同结果”的需求,绝大多数人的第一反应是写 IF:等于 1 就返回这个,等于 2 就返回那个,等于 3 再返回另一个,条件一多就变成一长串嵌套 IF,写到第七层时连自己都看不懂,改一个分支要在一堆括号里找半天。其实 Excel 里有两个函数专门解决这类“按序号选一个”的问题:CHOOSE 按序号从一串候选值里挑一个出来,INDEX 按行列号从一片区域里取一个出来。它们共同的特点是能把多层嵌套 IF 压成一层,而且候选值维护起来直观得多。

这两个函数都是老函数,所有 Excel 版本都支持,这一点在需要兼容旧环境时非常有价值。同时它们在新版本里也没有被淘汰,反而因为动态数组的加入变得更强大:INDEX 可以配合 SEQUENCE 一次取出多行,CHOOSE 可以把多个数组拼成一个二维数组,还能用来做“选择不同的计算方式”。本篇会把两者的用法、典型场景和相互替代关系讲清楚,让你在遇到分支需求时,第一时间想到的不再是嵌套 IF。

一、CHOOSE:按序号挑一个

CHOOSE分支选择流程

CHOOSE 的语法写在下面的代码块里,第一个参数是序号,后面是若干个候选值。

CHOOSE(index_num, value1, [value2], ...)

第一个参数 index_num 是序号,必须是 1 到 254 之间的数字,也可以是算出数字的公式。后面的 value1、value2 等是候选值,可以是常量、单元格引用、区域,甚至可以是完整的公式。函数会返回第 index_num 个候选值。比如 =CHOOSE(2, "华东", "华南", "华北") 返回“华南”;=CHOOSE(H1, B2, C2, D2) 根据 H1 的值返回 B2、C2 或 D2 中的一个。

它替代嵌套 IF 的效果非常直观。原本要写成 =IF(A1=1,"待处理",IF(A1=2,"处理中",IF(A1=3,"已完成","未知状态"))) 的四层判断,用 CHOOSE 就是 =CHOOSE(A1, "待处理", "处理中", "已完成"),一层搞定,要加一个新状态就在后面追加一个参数。可读性提升非常明显,尤其在状态值超过三个的时候。

但有两个限制必须知道。第一,序号只能是 1 开始的正整数,不能是 0 也不能是负数,如果 A1 里是 0 或者大于候选值个数,函数会返回 #VALUE!。稳妥做法是外面套一层 IFERROR,写成 =IFERROR(CHOOSE(A1, ...), "未知")。第二,CHOOSE 会计算所有候选值,即使最终只返回其中一个——这一点在候选值是耗时的公式时会影响性能,也意味着如果某个候选值里有会报错的公式,整个 CHOOSE 都会报错,不会因为你没选中它就幸免。

CHOOSE 还有两个进阶用法值得掌握。第一个是选择不同的计算方式:=CHOOSE(H1, SUM(B2:B100), AVERAGE(B2:B100), MAX(B2:B100)),H1 填 1 就求和、填 2 就平均、填 3 就取最大值,等于把三个函数装进一个开关。第二个是把多个区域拼成一个新数组:=CHOOSE({1;2}, B2:B10, D2:D10) 会得到一个两列的数组,第一列是 B2:B10、第二列是 D2:D10,这个写法在需要动态组合列时很有用,也为后面理解 CHOOSECOLS 打下基础。

二、INDEX:按位置从区域里取

INDEX取值两种配法

INDEX 有两种形式,写在下面的代码块里,日常主要用数组形式。

INDEX(array, row_num, [column_num])
INDEX(reference, row_num, [column_num], [area_num])

第一种数组形式最常用:array 是一片区域,row_num 是取第几行,column_num 是取第几列,省略列号时返回整行。比如 =INDEX(A2:D500, 10, 3) 取这个区域里第 10 行第 3 列,也就是 C11;=INDEX(A2:D500, 10) 返回第 10 行整行,需要作为数组使用。第二种引用形式支持多个不连续的区域,用第四个参数 area_num 指定用第几个区域,这种形式用得很少,了解一下即可。

INDEX 有几个特别有用的特性。第一,行列号可以是 0,写 0 表示取整行或整列:=INDEX(A2:D500, 0, 3) 返回整个第 3 列,也就是 C2:C500。这个特性配合冒号运算符能构造动态区域:=SUM(INDEX(A:A,10):INDEX(A:A,20)) 对 A10 到 A20 求和,而这两个端点都是算出来的,可以用来替代 OFFSET 的部分场景且不易失。

第二,行列号可以是数组,一次取出多个值:=INDEX(B2:B500, {1;3;5}) 会返回第 1、3、5 行的三个值并自动溢出。配合 SEQUENCE 就能做到“每隔 N 行取一个”:=INDEX(B2:B500, SEQUENCE(50,1,1,3)) 取第 1、4、7 行共五十个值。这是 INDEX 在动态数组时代焕发新生的关键,过去它需要按 Ctrl+Shift+Enter 才能返回数组,现在直接回车即可。

第三,INDEX 不是易失函数,这一点让它在很多场景下优于 OFFSET 和 INDIRECT。它不会因为任何重算而反复计算,插入删除行时引用范围也会自动调整,是“按位置取值”这个需求上最稳妥的选择。这也是为什么几乎所有 Excel 进阶教材都会建议:能不用 OFFSET 就不用,改用 INDEX。

第四,INDEX 和 MATCH 的组合是经典的二维查找方案,写法是 =INDEX(C2:M500, MATCH(H1, A2:A500, 0), MATCH(H2, C1:M1, 0)),先用 MATCH 分别算出行号和列号,再用 INDEX 取值。它支持任意方向的查找、不会因为插入列错位,在 XLOOKUP 出现之前是事实上的标准答案。即使在今天,需要兼容 Excel 2019 或更早版本时,这仍然是首选方案。

把这两个函数放在一起讲,是因为它们解决的是同一类问题的两个侧面:CHOOSE 的候选值是“你手写在公式里的”,INDEX 的候选值是“躺在某个区域里的”。所以当候选值少、固定、不会变时,用 CHOOSE 把值直接写进公式最省事;当候选值多、会变、需要别人维护时,把它们放进一片区域再用 INDEX 定位才是正确做法。判断标准就是一句话:这些值以后会不会被人改动。会改就放进区域,不会改就写进公式。

三、五个能直接抄的写法

下表列出了五种最常见的分支与定位需求。第二行和第四行是最能体现“不写 IF 也能分支”的两个。

需求 可复制的写法 要点
按序号选状态名 =CHOOSE(A2, "待处理", "处理中", "已完成") 替代三层嵌套 IF
按序号选计算方式 =CHOOSE(H1, SUM(B2:B100), AVERAGE(B2:B100), MAX(B2:B100)) 候选值可以是函数
按行列号取一个值 =INDEX(A2:D500, 10, 3) 行列号从区域左上角数起
取整列做动态求和 =SUM(INDEX(A:A,10):INDEX(A:A,20)) 两个 INDEX 用冒号连接
隔 N 行取一批值 =INDEX(B2:B500, SEQUENCE(50,1,1,3)) 行列号可以是数组

第二行值得多说一句:这种“用 CHOOSE 切换计算方式”的写法,在做看板时非常实用。你可以放一个下拉菜单让用户选择“汇总方式”,选项分别是求和、平均、最大值、计数,公式写成 =CHOOSE(H1, SUM(数据), AVERAGE(数据), MAX(数据), COUNT(数据)),一个下拉就能切换四种统计口径。唯一的注意点是前面提过的:所有候选值都会被计算,如果数据量很大会有一些额外开销。

第五行的思路可以扩展成很多花样。要倒序取一列,写成 =INDEX(B2:B500, SEQUENCE(COUNTA(B2:B500),1,COUNTA(B2:B500),-1));要随机取 10 个不重复位置,配合 SORTBY 和 RANDARRAY 生成下标流再用 INDEX 取。这类“先造下标流,再统一取值”的模式,是 INDEX 在动态数组时代的典型用法,比写一堆 IF 或者辅助列优雅得多。

四、真实场景

第一个场景是状态映射与看板切换。前面说过 CHOOSE 能把多层 IF 压成一层,最典型的应用就是把数据库里存的数字状态码翻译成人能读的文字。很多系统导出的状态是 1、2、3、4,报表里需要显示成中文,用 =CHOOSE(状态, "新建", "审批中", "已通过", "已驳回") 一次搞定,而且新增状态时只在末尾加一个参数,不用动括号结构。

第二个场景是动态汇总口径切换。做看板时经常要让用户选择“本月、本季、本年”三种口径,用 CHOOSE 可以写成 =CHOOSE(H1, SUMIFS(金额, 日期, ">="&EOMONTH(TODAY(),-1)+1), SUMIFS(金额, 日期, ">="&EOMONTH(TODAY(),-4)+1), SUMIFS(金额, 日期, ">="&DATE(YEAR(TODAY()),1,1)))。虽然看着长,但结构清晰,每个分支是一个完整的计算,比嵌套 IF 好维护得多。

第三个场景是二维查找。前面提到的 INDEX 加 MATCH 组合,在交叉表里取某个行列交叉点的值,是 INDEX 最经典的用途。比如一张表是产品横向、月份纵向,要取“产品 C 在三月的值”,写成 =INDEX(B2:M20, MATCH("三月", A2:A20, 0), MATCH("产品C", B1:M1, 0))。如果用的是新版本,这个场景用 XLOOKUP 嵌套两次也能实现,但 INDEX 加 MATCH 在所有版本都能用。相关对比可以看 XLOOKUP:一次写对的全能查找。

第四个场景是构造动态区域给图表或者名称用。用 =SUM(INDEX(A:A,10):INDEX(A:A,20)) 这种两个 INDEX 夹一个冒号的写法,可以做出和 OFFSET 一样的动态区域,但不易失。定义名称时写成 =INDEX(Sheet1!$A:$A,10):INDEX(Sheet1!$A:$A,COUNTA(Sheet1!$A:$A)),效果等价于 OFFSET 的动态高度区域,性能更好。这是在新老版本之间折中的一个好办法。

第五个场景是把 CHOOSE 当成小型查找表。当候选值不多且都是常量时,=CHOOSE(MATCH(H1, {"华东";"华南";"华北"}, 0), 1.1, 1.05, 0.95) 可以根据大区返回不同的系数,完全不需要建一张对照表。好处是公式自包含、复制时不依赖外部区域;坏处是系数改起来要动公式,不如对照表直观。判断标准很简单:候选值会不会经常变,会变就建对照表,不会变就用 CHOOSE 省事。

第六个场景是做“公式级的配置开关”。有些模板需要在不同的使用场景下走不同的逻辑,比如有的分公司按含税价算、有的按不含税价算,有的按自然月统计、有的按财务月统计。与其维护两份模板,不如在配置表里放几个开关值,主公式用 CHOOSE 根据开关选择对应的计算分支。这样一份模板服务多个场景,改配置即可切换,代价是公式会变长、可读性下降,所以开关数量最好控制在三四个以内,再多就该考虑拆分成不同的工作表了。

五、翻车提醒与版本说明

第一,CHOOSE 的序号超出范围会返回 #VALUE!。比如只有三个候选值却传了 4,或者传了 0。做模板时建议外面套 IFERROR 给一个默认分支。第二,CHOOSE 会计算所有候选值,如果某个候选值里有耗时公式或者会报错的公式,即使没被选中也会影响结果和性能。遇到这种情况,改用 IF 的短路特性或者查表法反而更安全。

第三,INDEX 的行列号是从区域左上角数起的,不是工作表行号列号。=INDEX(C2:F500, 2, 1) 里的第 2 行指的是 C2:F500 里的第二行,也就是工作表的第 3 行。这个坑和 SORT 的列号是一样的,习惯从区域左上角数起就不会错。第四,INDEX 的行列号写 0 时返回整行或整列,直接放在普通单元格里会只显示第一个值,需要作为数组使用或者放进聚合函数里。

第五,INDEX 加 MATCH 做查找时,MATCH 的第三个参数必须写 0 表示精确匹配,省略时默认是 1(近似匹配),要求查找列已排序,否则会返回错误结果且不报错。这是 INDEX 加 MATCH 组合最常见的翻车点,务必显式写 0。第六,MATCH 找不到时会返回 #N/A,需要提示信息时外面套 IFERROR。

版本方面,CHOOSE 和 INDEX 都是非常古老的函数,从 Excel 最早的版本就有,任何版本都能用,包括在老旧的 Excel 2010 甚至 2007 上。这意味着它们是写“兼容性模板”时的可靠选择:当你不确定对方用什么版本时,用 INDEX 加 MATCH 代替 XLOOKUP,用 CHOOSE 代替 IFS,是最保险的做法。本书第 4 章会专门讲版本对照,而 多条件查找的四种解法对比 一篇里也会横向比较这些方案在新旧版本下的取舍。

需要提醒的是,CHOOSECOLS 和 CHOOSEROWS 是两个名字相似但不同的新函数,它们只在 Microsoft 365 和 Excel 2021 之后才有,用法是从一个数组里挑出指定的几列或几行,比如 =CHOOSECOLS(A2:F500, 2, 4)。老版本里要实现同样的效果,可以用 =INDEX(A2:F500, 0, 2) 逐列取,或者用 CHOOSE 配常量数组拼装。命名上的相似性容易混淆,记住一点:CHOOSE 是“从参数里选一个”,CHOOSECOLS 是“从数组里挑几列”。另外,如果你要挑的列本身来自一份去重后的清单,建议先读 UNIQUE:去重不用再点删除重复项,用动态数组生成清单再用 INDEX 定位,比手工维护对照表省心得多。

常见问题

CHOOSE 的候选值里能放区域吗?

可以,而且这是它比较隐秘但很有用的一个能力。=CHOOSE(H1, B2:B10, D2:D10, F2:F10) 会根据 H1 返回三片区域中的一整片,放进 SUM 或者 AVERAGE 里就能对不同区域做统计。更进一步,写成 =CHOOSE({1;2}, B2:B10, D2:D10) 会把两片区域纵向拼成一个两行的数组,=CHOOSE({1,2}, B2:B10, D2:D10) 则横向拼成两列。这个用法在没有 HSTACK 和 VSTACK 的老版本里,是拼接数组的少数可行办法之一,遇到需要兼容旧环境时可以派上用场。

CHOOSE 和 IF 该用哪个?

看分支的数量和分支的内容。两三个分支、每个分支逻辑不同,用 IF 没问题;四个以上分支、且分支只是“返回不同的常量或者不同的简单计算”,用 CHOOSE 明显更清爽。另一个判断标准是:如果你的判断条件是“等于 1 就怎样、等于 2 就怎样”这种纯序号映射,那 CHOOSE 天生就是为它设计的;如果判断条件是一堆大小比较或者多条件组合,CHOOSE 就无能为力了,只能用 IF 或者 IFS。

CHOOSE 的参数个数有上限吗?

有,最多 254 个候选值,也就是说序号只能是 1 到 254。日常使用几乎不会触及这个上限,但如果你打算用它做一张很大的对照表,就要注意了。超过这个规模时,正确做法是建一张真正的对照表区域,然后用 VLOOKUP、XLOOKUP 或者 INDEX 加 MATCH 去查。把大量数据硬塞进公式里,无论用什么函数都会让公式变得难以维护,这不只是参数上限的问题。

INDEX 能不能一次返回多个值?

可以,只要行列号写成数组形式。比如 =INDEX(B2:B500, {1;3;5}) 返回第 1、3、5 行三个值并自动溢出;=INDEX(B2:B500, SEQUENCE(50,1,1,3)) 返回隔三行取一个的五十个值。在 Microsoft 365 里直接回车即可,在老版本里需要按 Ctrl+Shift+Enter 并且要预先选中足够大的区域。此外把行号或列号写成 0 可以取整行或整列,配合冒号运算符还能构造动态区域,是替代 OFFSET 的常用技巧。

为什么 INDEX 加 MATCH 查出来的值不对?

最常见的原因是 MATCH 的第三个参数没写 0。MATCH 的完整写法是 MATCH(lookup_value, lookup_array, [match_type]),第三个参数省略时默认是 1,表示近似匹配,要求查找区域已按升序排列,否则会返回错误的位置且不报错。所以做精确查找时一定要写成 MATCH(H1, A2:A500, 0)。第二个常见原因是查找值带了空格或者类型不一致,加一层 TRIM 或者统一类型就能解决。

INDEX 取出来的值能继续参与计算吗?

完全可以,它返回的就是普通的值或者引用。比如 =INDEX(B2:D500, 3, 2)*1.1 直接对取出的值做运算,=SUM(INDEX(B2:D500,0,2)) 对取出的整列求和,=INDEX(B2:D500, MATCH(H1,A2:A500,0), 2)&"元" 把结果拼上单位。把 INDEX 当成“可计算的地址”来用,能省掉大量中间单元格。真正需要注意的是它的行列号从区域左上角数起,写错了不会报错只会取到别处的值,所以写完最好用一组已知结果的数据核对一遍。

老版本里没有 CHOOSECOLS,怎么挑出指定的几列?

两条路。第一条是用 INDEX 逐列取再并列:=INDEX(A2:F500, 0, 2) 取第二列,然后写多个这样的公式横向排列;如果想在一条公式里完成,用 CHOOSE 配常量数组拼装:=CHOOSE({1,2}, INDEX(A2:F500,0,2), INDEX(A2:F500,0,4)),就得到一个两列的新数组。第二条是借助辅助列或者 Power Query 先整理出需要的列顺序。如果只是临时处理,直接插入几个空列把需要的列复制过去也完全可行,不必强求一条公式解决。