UNIQUE:去重不用再点删除重复项

如果你还在用“数据”选项卡里的“删除重复项”按钮做去重,那下面这三种麻烦你大概率都撞过:第一,原始数据被就地改掉了,回头想对账时找不到依据,只能靠撤销或者事先另存一份;第二,源数据又加了几行,上次去好的结果不会跟着变,还得再点一遍按钮;第三,想按客户名称去重、却要保留这个客户最近那一条完整记录,按钮根本做不到,只能先排序再手工挑。UNIQUE 的价值就在这三个缝里:它是一个公式,源数据变了结果自动跟着变,原始表一行不动,而且按哪一列去重、保留哪几列、出现几次才算数,全都由参数说了算。

动态数组是这几年 Excel 最大的一次能力升级,而 UNIQUE 是其中最容易上手、也最立竿见影的一个。你在一个单元格里写下公式、回车,结果会自动溢出到下方和右方的单元格里,占满它需要的那片区域。不需要按 Ctrl+Shift+Enter,也不需要提前选中一片区域再填充。理解了这个溢出机制,本书第 1 章后面几篇讲的 FILTER、SORT、SEQUENCE 都是同一套心智模型,学起来会非常快。本篇所有区域都按“第 1 行是表头、数据从第 2 行到第 500 行”来写,你换成自己的区域即可直接套用。

一、三个参数,一次说清

Excel UNIQUE去重函数三个参数

UNIQUE 的完整语法只有三个参数,写在下面这个代码块里,其中方括号表示可以省略。

UNIQUE(array, [by_col], [exactly_once])

第一个参数 array 是要去重的区域,可以是一列,也可以是多列多行的矩形区域。第二个参数 by_col 决定比较方向,省略或者写 FALSE 时按行比较,也就是最常见的“整行内容相同才算重复”;写 TRUE 时改成按列比较,适合那种数据横向铺开、每一列才是一条记录的怪表,日常很少用到。第三个参数 exactly_once 是最容易被忽略、但一旦用上就回不去的一个:省略或者写 FALSE 时返回去重后的全部唯一值;写 TRUE 时只返回在整个区域里恰好出现过一次的值,也就是自动帮你把所有重复项都筛掉,只留下孤本。

这三个参数里,真正把 UNIQUE 从“去重按钮”升级成“分析工具”的是第三个。举个例子,=UNIQUE(B2:B500,,TRUE) 会直接告诉你:在五百条订单里,哪些客户只下过一次单。这个结果过去要用 COUNTIF 加一整列辅助列、再筛选才能做出来,现在一个公式就够,而且新增数据时结论自动更新。注意中间那个参数是空着的,两个逗号之间什么都不写,Excel 会按 FALSE 处理,这种留空写法在动态数组家族里非常普遍。

第二个参数虽然用得少,但遇到横向表时能救命。有些从系统导出的报表是月份横着排的,A2 到 L2 是同一个客户十二个月的数值,此时 =UNIQUE(A2:L50, TRUE) 会按列比较,把重复的月份列去掉。判断要不要用它的办法很简单:看你的记录是竖着一条还是横着一条,竖着就省略,横着就写 TRUE。绝大多数人一辈子只用到按行去重,但知道有这个开关,遇到怪表时就不会手足无措。

二、五个能直接抄的写法

下面这张表覆盖了日常九成的去重需求,左边是你要做的事,中间是可复制的公式,右边是需要注意的地方。表格本身可以直接对着抄,改一下区域就能用。

你想做的事 直接抄的公式 需要注意
单列去重取清单 =UNIQUE(B2:B500) 最常用,得到不重复的客户名单
多列组合去重 =UNIQUE(B2:D500) 三列内容完全一致才算同一条
去重后顺手排序 =SORT(UNIQUE(B2:B500)) 外套一层 SORT 即可,见 SORT 篇
只留出现过一次的 =UNIQUE(B2:B500,,TRUE) 第三参数写 TRUE,注意双逗号
统计不重复个数 =COUNTA(UNIQUE(B2:B500)) 不用单独做辅助列再计数

注意最后一行:UNIQUE 的结果本身是一个数组,可以直接塞进 COUNTA、ROWS、SUM 这些聚合函数里当参数用,不需要先把它落在单元格上再引用。这是动态数组最省心的地方,中间结果可以一直飘在公式里,工作表上干干净净。

还有一种写法值得单独记一下:跨列拼接后再去重。如果客户名称在 B 列、城市在 C 列,而你要的是“客户加城市”这个组合的唯一清单,可以写成 =UNIQUE(B2:B500&"|"&C2:C500),用竖线做分隔符避免两个字段连在一起产生歧义,之后需要拆开时再用文本拆分函数处理。同理,把一个区域压成一列也可以用 =UNIQUE(TOCOL(B2:D500)),TOCOL 会先把三列并成一列再去重,适合那种数据散落在好几列、但含义相同的表。

再往下走一步,如果你想按客户去重、但只保留每个客户金额最大的那一条,UNIQUE 单独做不到,需要和排序函数配合。做法是先用 =SORTBY(B2:D500, D2:D500, -1) 把金额从大到小排好,再在外面套 UNIQUE 取首次出现的行。因为 UNIQUE 保留的是首次出现的那一行,所以先排序就成了“去重时保留谁”的开关,想保留最近日期的就把排序依据换成日期列,想保留最小金额的就把最后一个参数从 -1 改成 1。这个套路几乎可以覆盖所有“每组取一条”的需求。

三、真实场景:从三千行订单里提取客户清单

假设 A 列到 F 列是一张订单明细表:A 列订单号、B 列客户名称、C 列城市、D 列金额、E 列销售员、F 列下单日期,数据从第 2 行到第 3000 行。月末你要给每个销售员发一份他名下的客户清单,过去是逐个筛选、复制、粘贴,现在可以在一张新表的 A1 里写下客户去重,B1 里写下城市去重,两列一次成型,源数据增加时两边一起变长。

更进一步,把去重结果当成下拉菜单的数据源。选中要填客户名称的那一列,打开“数据验证”,允许条件选“序列”,来源框里直接写 =$A$1#,末尾那个井号叫溢出引用运算符,代表 A1 溢出来的整片区域。这样一来,客户名单增加时下拉菜单会自动变长,不用每隔几个月去改一次数据验证的来源区域。这是 UNIQUE 和数据验证组合后最舒服的用法之一,很多模板里都在用。

第三个常见场景是给图表准备数据源。做柱状图时如果横轴上出现重复的名称,柱子会被拆成两根,看起来像是数据错了。解决办法是把图表的数据源改成 UNIQUE 的溢出区域,再用 SUMIF 算出每个名称对应的一条数值,两列一起喂给图表。这样图表会随源数据自动增减系列,比每次手工调整数据源省事得多,而且不用担心漏掉新出现的分类。

还有一个高频组合是统计维度个数。老板问这个月一共服务了多少家不同客户,用 =COUNTA(UNIQUE(B2:B3000));问某个销售员覆盖了多少个城市,用 =COUNTA(UNIQUE(FILTER(C2:C3000, E2:E3000="张伟")))。后一个写法把筛选和去重串在了一起,是动态数组最典型的用法:由内向外读,先筛出张伟的行,再从这些行里取城市的唯一值,最后数一下有几个。想系统掌握这种由内向外的嵌套思路,建议接着读 FILTER:按条件把行整条捞出来。

四、翻车提醒

UNIQUE去重翻车与防护

第一,溢出区域被挡住会报 SPILL 错误。UNIQUE 会把结果往下铺,如果下方任意一个单元格里已经有内容,公式不会覆盖它,而是返回 #SPILL!。解决办法很简单:清空下方那片区域,或者把公式挪到一块空地上。另外如果公式被放在了超级表(Ctrl+T 建的表)内部,也会因为行数固定而溢出失败,把公式放到表外即可。

第二,空单元格会被算作一个唯一值。如果 B2:B500 里有空白格,UNIQUE 的结果里会出现一个 0 或者一个空白项,看着像是多了一行垃圾数据。处理办法是先用筛选函数过滤掉空值,写成 =UNIQUE(FILTER(B2:B500, B2:B500<>""))。这个组合建议直接背下来,属于最高频的搭档写法之一。

第三,大小写不敏感但空格敏感。UNIQUE 默认不区分大小写,Apple 和 apple 会被当成同一个值;但首尾空格会导致本该相同的两个值被判成两项,肉眼完全看不出区别。如果数据是从业务系统导出的,建议先套一层清理函数:=UNIQUE(TRIM(B2:B500))。遇到全角半角混排的情况,还要再考虑用 SUBSTITUTE 统一。

第四,不要对整列引用。=UNIQUE(B:B) 会把一百多万行全部纳入计算,其中绝大多数是空的,文件会明显变卡,结果里还会多出一项空白。老老实实写 B2:B500,或者把源数据转成超级表后用结构化引用,让区域跟着数据走。

第五,大数据量下要留意重算开销。如果一张表里放了十几个 UNIQUE 公式,每个都对着上万行区域,每次改动任意一个单元格都会触发全部重算,输入时会明显感到卡顿。缓解办法是把去重结果集中在一两张辅助表上统一计算,其他位置用井号引用它,而不是每个地方都各自写一遍 UNIQUE。真到了几万行还卡的程度,就该考虑用 Power Query 做去重了,它只在刷新时算一次。

第六,结果区域的大小不会预留。UNIQUE 溢出多少行是由源数据决定的,你不能指望它固定占十行然后空着几行给以后用。如果下方还要放别的内容,建议中间至少空出一列或者把结果单独放一张工作表,避免以后新增客户时撞车报 #SPILL!。

五、版本要求与老版本替代

UNIQUE 需要 Microsoft 365 订阅版,或者 Excel 2021 及以后的永久授权版本。Excel 2019、Excel 2016 以及更早的版本都没有这个函数,打开文件时公式会被保留但显示为 #NAME?,部分老版本还会把公式显示成带 _xlfn. 前缀的样子,同样无法计算。公司里如果还有人在用 Excel 2016,你发出去的文件对他就只有一堆错误值,这一点在协作前一定要确认。

老版本要做同样的三件事,替代方案分别是:要生成不重复清单,用“数据”选项卡里的“高级筛选”,勾选“选择不重复的记录”,可以选择复制到别处,缺点是不会自动更新,每次都得重新点一次;要统计不重复个数,用数组公式 =SUMPRODUCT(1/COUNTIF(B2:B500,B2:B500)),注意如果区域里有空单元格会返回 #DIV/0!,需要写成 =SUMPRODUCT((B2:B500<>"")/COUNTIF(B2:B500,B2:B500&"")) 来规避;要每组取一条,只能先排序,再用 COUNTIF 做一个辅助列标记首次出现的行,然后按辅助列筛选复制。

还有一个折中做法值得记住:把源数据建成超级表,再插入一张数据透视表,把要去重的字段拖到行区域。透视表本身就会做去重,刷新即可更新,在老版本里这是最接近“动态去重”的方案,而且不依赖任何新函数。缺点是结果格式受透视表样式限制,不能随意在旁边继续写公式,也不能直接拿来当数据验证的来源。

判断手上这台 Excel 到底支不支持,最快的办法是随便找个空单元格写上 =UNIQUE({1;1;2}),能返回两个值就说明支持,返回 #NAME? 就说明版本不够。注意即使桌面版支持,Excel 网页版和手机版对动态数组的支持也更晚一些,手机上通常只能查看溢出结果而不能编辑公式,做模板时要考虑这个差异。

如果你需要和同事协作,最稳妥的习惯是先问一句对方用的什么版本,再决定要不要上动态数组。要判断手上这个函数在自己版本里到底有没有,最快的办法是随便找个空单元格写上 =UNIQUE({1;1;2}),能返回两个值就说明支持。更多关于新旧版本能力的对照,可以配合本书后续章节一起看,本篇讲的是动态数组家族里最基础的一环,吃透之后再读 SORT 和 SORTBY:排序交给公式 和 多条件查找的四种解法对比 会顺畅很多。

常见问题

UNIQUE 和“删除重复项”按钮到底该选哪个?

看你要的是一次性清理还是长期维护。如果只是临时看一眼有哪些类别,按钮更快;如果这份清单要持续用、要拿去做下拉菜单或者图表数据源,那就必须用公式。判断标准很简单:源数据以后还会不会再变。会变就用 UNIQUE,不会变用哪个都行。另外按钮会直接修改原表,做完无法撤销到中间状态,涉及财务数据时建议先在副本上操作,或者干脆只用公式不动原表。

UNIQUE 的结果能直接拿来做数据验证的下拉菜单吗?

可以,而且这是它最好用的场景之一。在数据验证的来源框里写 =$A$1#,其中 A1 是 UNIQUE 公式所在的单元格,末尾的井号表示引用整个溢出区域。之后源数据新增客户,下拉菜单会自动变长,无需回头修改数据验证设置。注意来源不能直接写公式本身,必须是单元格地址加井号的形式,否则 Excel 会提示来源无效。另外如果去重结果里可能包含空值,记得先用 FILTER 过滤掉,否则下拉菜单里会多出一个空白选项。

为什么我的 UNIQUE 返回了一个多余的空值?

两个常见原因。一是区域里有真正的空单元格,UNIQUE 会把空值当作一个唯一值保留下来;二是你把区域写得比实际数据大,比如只用了三百行却写到了第五百行。解决办法是用 =UNIQUE(FILTER(B2:B500, B2:B500<>"")) 先过滤再去重。如果空单元格里其实是公式返回的空字符串,这个条件依然有效;但如果返回的是数字 0,就需要额外加一层判断条件,把等于 0 的行也排除掉。

我想按客户去重、同时把金额求和,UNIQUE 能做到吗?

UNIQUE 本身只负责去重,不负责汇总,但它和 SUMIF 组合起来非常顺手。写法是先取客户清单 =UNIQUE(B2:B500) 放在 H2,再在 I2 写 =SUMIF($B$2:$B$500, H2#, $D$2:$D$500),结果会自动溢出成一列和清单等长的汇总值。如果用的是 Microsoft 365,也可以写成 =LET(c, UNIQUE(B2:B500), HSTACK(c, SUMIF(B2:B500, c, D2:D500))),一条公式同时给出客户列和金额列两列结果,不需要占用中间单元格。

去重结果没有跟着源数据自动更新怎么办?

先确认计算模式是不是被改成了手动。在“公式”选项卡里看“计算选项”,如果是“手动”,按一下 F9 就会重算,改回“自动”即可长期解决。另外检查源区域是不是写死成了 A2:A500:如果新增数据落在第 501 行,公式自然看不到它。建议把源数据转成超级表,然后把公式里的区域改成结构化引用,这样新增行会自动纳入计算范围。最后还有一种情况是工作簿被共享或者启用了旧的兼容模式,此时部分动态数组功能会被禁用,另存为新格式通常就能恢复。