
做数据分析的朋友谁没遇到过“把几十个Excel文件合成一个表”的时刻你可能每月要从各个门店报上来的销售明细里抓数据也可能手里攒了十几份不同月份的订单记录还有可能是从业务系统按日导出的增量数据。在PowerBI里如果只想到“导入数据”然后一个个文件手工复制那就太亏了——Power Query的文件夹导入功能本身就是为了这个场景设计的。它能在不打开Excel的情况下自动读取指定文件夹里的所有文件按统一的规则合并成一张表刷新时还能自动发现新加进来的文件。听起来很爽但如果你没搞懂它背后的合并机制很容易出现列错乱、日期变数字、刷新没反应这些“玄学错误”。这篇内容就是基于我自己的实战输出从数据准备、Power Query操作、M函数写法到常见踩坑排查把多文件读取合并这件事完整拆一遍适合刚开始用PowerBI做数据分析、或者已经做了几张报表但合并数据还靠人工处理的同学。1. 为什么多文件合并是PowerBI数据分析的必修课1.1 多文件数据场景远比你想象的多只要做过一段时间的报表你一定会遇到下面这几种情况电商公司每天从平台导出订单明细按日期保存成一个CSV一年下来就是365个文件。连锁门店每周发来各自销售报表每个门店一个Excel文件结构一样但数据分属不同区域。财务系统每个月结账后导出一份总账跨年跨月文件名有规律但没有合并视图。网约车、白酒、农产品这些行业的数据分析项目往往也是来自多个终端、多个城市、多个时间切片的历史数据文件。这些场景的共同特点是数据结构完全一致但数据被切分到了多个文件里。如果你不在PowerBI里做合并报表上看到的就是一个个孤立的文件只能在Excel里手动粘贴下一次数据更新又要重复劳动。在我看来多文件读取合并并不是什么高级功能而是一个数据分析师做数据模型前必过的“关卡”。谁跨过这一步谁才算是真正开始用PowerBI做自动化分析。1.2 手工复制粘贴不只是麻烦更是数据灾难手工合并至少存在四个致命问题漏文件。你只记得粘贴了前10个文件却忘了第11个最后报表汇总数字偏差你根本发现不了。列错位。文件的列顺序稍有变化粘进去之后门店名称跑到了订单号列数据分析结果直接失真。数据量一大就卡。几千行还好几十万个行级明细放在Excel里筛选、透视、刷新都慢得让人抓狂。无法自动化。每次业务方丢来一个新文件你都要手动打开、复制、粘贴、调格式本质上就是在浪费生命。使用PowerBI的多文件读取合并根本目标不是“省一次操作”而是建立一条可持续自动刷新的数据流水线。你只需要在文件夹里丢进新文件点一下“刷新”新数据就自动进入模型。这才符合数据分析从“一次性需求”变成“持续交付”的趋势。1.3 为什么很多人一开始就会搞砸PowerBI界面默认的“导入数据”和“合并文件”是两码事。如果你点击获取数据一次性选中多个Excel文件并直接打开PowerBI会为每个文件各自创建一个查询表并不会自动把它们拼成一张表。所以很多人第一次操作后发现左侧出现了十几张表报表还得用DAX把多张表关联起来这显然不是我们想要的效果。正确逻辑是利用Power Query的文件夹读取能力先把“每个文件的内容”变成“文件列表中的一列数据”然后再统一展开、追加、合并。理解了这一层你就不会用错功能。2. 先把文件“收拾”好能避掉一半的坑2.1 文件夹设计是第一步不要小看文件夹的规划。我见过不少同学直接在桌面上建一个Word文档叫“最新报表”然后把文件、截图、说明、备份全丢进去最后Power Query扫描出来什么乱七八糟的东西都有。建议每个合并任务对应一个顶层文件夹例如D:\Data\Sales。所有需要合并的原始数据文件都直接放在这个目录下如果确实需要按月份归档可以建子文件夹。注意Power Query中的Folder.Files()函数会默认递归读取所有子文件夹中的文件这既是好事也是隐患。好事是嵌套结构也能自动处理隐患是你如果把不相关的文件也放在里面它们会被一并合并。我自己的习惯是只有一个合并任务就只有一个文件夹文件夹里只放待合并的原始数据文件。把“说明.txt”、“备份文件夹”、“原来手工合并好的总表”都挪到另一个目录避免合并范围被污染。2.2 统一列名和列顺序Power Query自动合并且展开数据时默认以“第一个文件”的表头作为标准列名。如果第二个文件的列名不一样比如一个叫“门店”另一个叫“门店名称”合并后就会出现两个不同的列或者数据错位。处理方式在最初准备阶段用Excel打开几个文件确认第一行表头完全一致。删除文件里多余的标题行、说明行、合并单元格。尽量保持每个文件里核心列的顺序一致尽管这一条在现代M代码中不是必须但能让排查问题更简单。如果你控制不了别人发来的表格可以在Power Query中通过“提升标题”“删除空行”“重命名列”等步骤做标准化。但记住这些清洗步骤本身需要计算量文件越多性能消耗越大。能在源头解决的就不要留给Power Query。2.3 提前统一数据类型合并时Power Query会按照示例文件推断数据类型。如果某个文件的“销售额”列带着人民币符号或空格另一个文件则是纯数字展开后很可能出现“无法转换”的错误。日期字段尤其容易踩坑Excel里的日期在不同区域设置下会被处理成数字序列例如45269代表某一天。建议在生成原始文件时就确保数字列没有千分位逗号日期列是真正的日期类型而不是看起来像日期的文本。CSV文件更要注意直接导出CSV的时候日期格式会跟随计算机的区域设置最好统一成yyyy-MM-dd格式能省掉后面很多麻烦。2.4 避免在合并范围内出现汇总文件或计算文件我知道很多人会习惯性地把一个“合并好的总表”也放在同一个文件夹里。然后Power Query把它当成一个普通文件读进去合并结果里既有明细又有汇总行数翻倍指标也全乱套。所以我强烈建议合并文件夹里只能有“原始数据文件”任何中间产物、汇总文件、输出文件都要放到另一个目录。如果你已经犯了这个问题不要试图在查询里写复杂条件去排除直接把文件移走最干净。3. Power Query中的三种多文件读取合并方式3.1 方式一使用【获取数据 → 文件夹】纯界面操作这是最推荐给新手的操作路径不需要写一串M函数Power Query会自动生成绝大部分代码。具体步骤在Power BI Desktop中选择“主页 → 获取数据 → 文件夹”。输入或浏览到你的数据文件夹路径点击“确定”。预览窗口里会出现一个表每一行代表一个文件包含Name、Extension、Date Modified等元数据列。在底部找到“合并文件”按钮点击它。Power Query会生成一个“转换示例文件”查询并在预览区域展示合并后的表。在示例文件中选中需要保留的列再点击“确定”Power Query会自动展开所有文件的数据。最后加载到数据模型。这个方式的优点是零代码界面友好。缺点是它依赖“示例文件”的结构其他文件如果有列差异就会报错。所以它更适合文件结构完全统一且数量不太大的场景。3.2 方式二用M函数写可反复复用的合并逻辑当你对Power Query有了一定理解我更建议你手写M语言。这样做的原因有三个第一每次刷新时都重新扫描文件夹逻辑透明第二可以自由添加筛选条件比如排除临时文件第三支持把整个读取逻辑封装成自定义函数不同项目之间直接复用。这是一个合并文件夹内所有Excel工作簿所有工作表的M代码模板let 数据路径 C:\Data\Sales, 源文件表 Folder.Files(数据路径), // 过滤出Excel文件并排除Excel打开时生成的临时锁文件以~$开头 筛选文件 Table.SelectRows(源文件表, each [Extension] .xlsx and not Text.StartsWith([Name], ~$)), // 读取每个工作簿内容Excel.Workbook会返回一张包含多个工作表信息的嵌套表 读取工作簿 Table.AddColumn(筛选文件, 工作簿内容, each Excel.Workbook([Content])), // 展开工作簿内容提取出每个Sheet的数据保留Sheet名和源文件名 展开工作簿 Table.ExpandTableColumn(读取工作簿, 工作簿内容, {Data, Name}, {数据, 工作表名}), // 展开数据列这里用第一个表的所有列名作为展开列名注意列名一致性 展开数据 Table.ExpandTableColumn(展开工作簿, 数据, Table.ColumnNames(展开工作簿[数据]{0}), Table.ColumnNames(展开工作簿[数据]{0})), // 添加一列标记每条数据来自哪个文件方便追溯 添加来源 Table.AddColumn(展开数据, 来源文件, each 源文件表[Name]), // 移除不需要的元数据列 删除多余列 Table.SelectColumns(添加来源, {门店, 订单号, 销售额, 日期, 来源文件}) in 删除多余列说明一下Table.ColumnNames(展开工作簿[数据]{0})这行代码取的是“第一张表”的列名列表。只要其余表的列名和第一张表一致这段代码就是安全的。如果列名有差异你就会遇到异常后面我会专门讲怎么处理。3.3 方式三合并文件夹下所有CSV文件CSV文件的合并逻辑和Excel稍有不同。CSV没有工作簿和工作表的概念直接用Csv.Document()读取文本内容即可。下面是一个适用于CSV合并的M代码片段let 源 Folder.Files(C:\Data\CSV), 筛选 Table.SelectRows(源, each [Extension] .csv), 读取CSV Table.AddColumn(筛选, 数据, each Csv.Document([Content], [Delimiter,, Encoding65001])), 展开数据 Table.ExpandTableColumn(读取CSV, 数据, Table.ColumnNames(读取CSV[数据]{0}), Table.ColumnNames(读取CSV[数据]{0})), 添加来源 Table.AddColumn(展开数据, 文件名, each [Name]) in 添加来源这里特别要注意两个点Encoding65001表示以UTF-8编码读取文件。如果文件是中文版Excel另存的CSV可能是GB2312编码这时要把Encoding参数改为936否则中文会乱码。Delimiter,要按实际分隔符调整。如果是制表符分隔的TXT文件改为Delimiter\t。如果文件夹里既有Excel又有CSV我不建议在一个查询中强行合并最好先按扩展名筛掉分别读取再用“追加查询”拼起来或者把原始文件统一格式。3.4 只要每个工作簿中的指定工作表有些Excel文件里有好几个Sheet可能包括“明细”、“汇总”、“参数”。如果不加处理Power Query会把所有Sheet都读进来导致合并结果里混入非明细数据。解决办法是在展开工作簿后添加一个筛选步骤只保留指定Sheet名筛选工作表 Table.SelectRows(展开工作簿, each [工作表名] 明细)然后继续展开。这样既能避免多余Sheet的干扰还能减少读取量提升刷新速度。我更推荐的做法是在示例文件查询里就把不需要的Sheet删除只保留要用的Sheet。这样后续合并所有文件时Power Query会自动沿用这个处理流程逻辑更加清晰。4. 实操整个合并流程从文件夹到加载4.1 场景假设与初始状态我这里拿一个门店销售明细合并来演示。假设文件夹路径为D:\Data\销售明细里面有多个Excel文件比如上海门店_2025-03.xlsx、北京门店_2025-03.xlsx每个文件的“明细”Sheet包含四列门店、订单号、销售额、日期。目标把这几个文件合并成一张表刷新时能自动包含新增文件并且能追溯每条数据的来源文件。4.2 第一步先做好示例文件查询Power Query的合并文件功能会要求选一个示例文件之后它用示例文件的结构去推断其他文件的结构。所以先在文件夹中选一个文件单独打开它应用如下变换使用“获取数据 → Excel工作簿”加载这个样例文件。在Power Query编辑器里选择“明细”Sheet。如果表头有杂项使用“将第一行用作标题”。选择所需列删除无关列。将“销售额”设为小数“日期”设为日期类型。完成之后你得到的是一个已经清洗干净的示例查询。把所有步骤命名清晰例如命名为示例文件后面合并时会很有用。4.3 第二步使用“合并文件”按钮生成批量读取回到之前的文件夹预览视图点击“合并文件”。此时Power Query会自动引用示例文件的每一步变换并生成类似下面的逻辑let 源 Folder.Files(D:\Data\销售明细), 读取Excel Table.AddColumn(源, 数据, each Excel.Workbook([Content])), 展开工作簿 Table.ExpandTableColumn(读取Excel, 数据, {Data, Name}, {SheetData, SheetName}), 筛选Sheet Table.SelectRows(展开工作簿, each [SheetName] 明细), 展开表内容 Table.ExpandTableColumn(筛选Sheet, SheetData, Table.ColumnNames(筛选Sheet[SheetData]{0}), Table.ColumnNames(筛选Sheet[SheetData]{0})) in 展开表内容在这个基础上我再加两步过滤掉~$开头的临时文件、增加“来源文件”列。你可以在高级编辑器中手动修改生成后的代码。4.4 第三步解决“列名不一致”的通用方案如果所有文件都是别人手工维护的难保列名一模一样。比如某个文件多了一列“备注”另一个文件少了“地区”列。遇到这种结构不齐的情况Table.ExpandTableColumn就会抛出找不到列名的错误。通用的解决方案是先获取所有文件所有表的列名并集然后给每张表补上缺失列最后再Table.Combine。下面是我在项目中稳定可用的一段M函数式逻辑let // 假设你已经有一个包含所有待合并表内容的列表所有表 所有表 展开后的表格列表, // 步骤1收集所有表的所有列名去重得到并集 所有列名 List.Union(List.Transform(所有表, each Table.ColumnNames(_))), // 步骤2定义补列函数遍历目标列名如果当前表没有该列就补上null 补列函数 (t as table, 目标列 as list) List.Accumulate(目标列, t, (state, col) if Table.HasColumns(state, col) then state else Table.AddColumn(state, col, each null)), // 步骤3对每一张表执行补列操作 补齐后的表列表 List.Transform(所有表, each 补列函数(_, 所有列名)), // 步骤4用Table.Combine将多张表垂直合并 合并表 Table.Combine(补齐后的表列表) in 合并表这套逻辑的核心是List.Union和Table.Combine。前者拿所有列名的并集后者要求每张表的列完全一致才允许合并所以必须先把缺失列补上。这个函数式片段虽然看起来有点抽象但实际用起来非常稳尤其适合列名经常变动的历史遗留文件。4.5 第四步加载到数据模型合并完成后点击“关闭并应用”把结果加载到Power BI数据模型。加载前我通常会检查一下如果不想把所有明细列都放进模型可以在查询设置里右击查询选择“加载到”取消加载部分非必要列。确认“隐私级别设置”不会阻止文件夹读取必要时在数据源设置中设为“忽略隐私级别”否则刷新时会弹权限提示。加载完成后后续只要有新文件放进原始文件夹你只要在Power BI Desktop中点击“刷新预览/刷新”Power Query就会自动扫描文件夹合并新增文件整个流程不需要任何人工复制粘贴。4.6 第五步合并结果验证做数据分析必须要验证不然一个错误的数据模型会让所有报表失真。我一般会用下面三招在文件夹里统计所有原始文件的数据行数然后和合并表的总行数做对比行数不一致就说明有漏文件或重复合并。查看“来源文件”列确认每个文件都被正常读取且没有出现重复文件。抽样检查几个关键订单号在源文件里搜索对比合并表中的对应记录。如果你的合并过程中使用过“追加查询”也要检查“表头是否重复”以及是否存在因为SheetName不一样而漏读的情况。验证完毕才进入下一步分析。5. 常见问题与排查技巧实录5.1 报错“无法合并文件因为列名不同”这是多文件合并里出现频率最高的错误。根本原因是Power Query在展开嵌套表格时用了第一个文件的列名作为标准列后面某些文件的列名不一致它就直接罢工。排错方法先用Excel批处理工具或者Power Query本身对比所有文件的第一行表头列出差异。如果差异不大直接把少数文件的列名改成标准列名重新保存。如果文件非常多就用上一节提到的“List.Union 补列函数 Table.Combine”方案让查询自动兼容列缺失问题。别试图去猜哪个文件有问题用Power Query做一个只返回列名列表的浅层查询比肉眼一个个打开快得多let 源 Folder.Files(D:\Data\销售明细), 筛选 Table.SelectRows(源, each [Extension].xlsx), 列名检查 Table.AddColumn(筛选, 表列名, each Table.ColumnNames(Excel.Workbook([Content]){0}[Data])) in 列名检查这个查询只读取每个工作簿第一个Sheet的列名不会加载全部数据排查速度非常快。5.2 表头错乱第一行不是标题很多手动维护的Excel文件里第一行可能是“XX报表”第二行是“日期2025年3月”第三行才是真正的表头。Power Query默认会把这些都当成列名于是合并后的数据出现大量Column1、Column2之类的占位列。解决办法在示例文件查询中把第一行以下的标题行删除。使用“将第一行用作标题”确保表头被正确识别。如果里面还有空行在展开数据后使用“删除空白行”。对于文件格式混乱的情况我强烈建议先和业务方约定格式数据表必须从A1单元格开始第一行为表头之后不能出现任何合并单元格。因为Power Query是结构化读取工具不是万能的Excel模拟器。5.3 日期变成了一串数字日期显示成45269一类的数字是多文件合并中最经典的翻车现场。Power Query的日期转换依赖文化设置和原文件类型当某个文件的日期列被误判为文本或数字时就会出现这种问题。解决思路在示例文件中把日期列手动改为“日期”类型并且确定格式。Power Query随后会用这个类型去匹配所有文件。如果已经产生了数字序列你可以在合并之后加一步转换日期 Table.TransformColumnTypes(合并表, {{日期, type date}})如果是从CSV文件读取可以通过Csv.Document的参数指定Culture比如[Culturezh-CN]避免本地化日期解析混乱。5.4 刷新后新增文件没有出现在结果中这种情况特别容易被忽略。原因通常包括新文件的扩展名和筛选条件不符。例如你只筛了.xlsx新文件却是.xls。新文件的Sheet名称和你的筛选条件不一致。你只保留“明细”Sheet但新文件Sheet叫“销售明细”。新文件被放在子目录中某些自定义查询使用了Folder.Contents()而不是Folder.Files()导致只读取顶层目录。新文件正在被另一个程序锁定导致Power Query读取失败。排查时把合并查询中的Sheet筛选条件暂时移除刷新看能不能看到新文件。如果不能进一步确认文件本身是否保存成功、是否被占用。这个过程通常两分钟就能定位。5.5 合并几十个大文件刷新非常慢如果你每次刷新要等十分钟说明需要优化了。性能瓶颈主要出现在“展开Excel.Workbook”这一步因为Power Query要和每个文件交互。优化建议只保留必要列不要在查询中展开所有列。只读取指定的Sheet不要展开整个工作簿。关闭“快速加载”功能避免刷新时长时间占用内存。如果数据超过几百万行建议用Power BI Dataflow预先合并或者干脆把所有源文件汇总到数据库表再让PowerBI连接数据库。如果每个文件几十MB考虑让业务方改成按天增量导出CSV而不是一次性输出超大Excel。5.6 空表或只有标题的文件导致合并后缺列一个文件只有表头没有任何数据行Power Query仍然会把它当成一个有效表参与展开。如果它的列数和标准列数不一致就会引发错误。解决办法在展开嵌套表之前增加过滤过滤空表 Table.SelectRows(展开工作簿, each Table.RowCount([SheetData]) 0)注意这个过滤必须作用在“SheetData未被展开”之前这样空表就被直接扔掉了。5.7 常见问题速查表故障现象可能原因解决办法合并报错列名不同各文件表头不一致统一列名或用List.Union补列合并后行数翻倍文件夹里有汇总文件将汇总文件移出原始文件夹日期变成数字区域设置或类型推断错误在示例文件里设置列类型为日期CSV中文乱码编码不统一指定Encoding65001或936合并后无新增文件过滤条件或Sheet名不匹配检查筛选条件和文件夹路径刷新慢文件多、列多、展开层级多精简列、关闭快速加载、使用Dataflow6. 一些值得长期使用的进阶技巧6.1 参数化文件夹路径把文件夹路径定义成Power Query参数会让你的项目更加灵活。操作方法是点击“开始 → 管理参数 → 新建参数”类型选择“文本”名字叫DataPath当前值设为D:\Data\销售明细。之后所有查询里的Folder.Files(C:\Data\销售明细)都改成Folder.Files(DataPath)。以后换季度、换项目只需要修改参数值再点击刷新全部查询就会自动指向新的文件夹。对于做月度数据看板的人来说这个技巧能节省大量重复操作。6.2 在合并结果中保留文件名和路径在明细数据上增加“来源文件名”“来源路径”列能让数据可追溯性大大提升。尤其是当报表结果出现异常时你可以快速定位到问题文件。在展开嵌套表之前通过原始文件列表中的Name和FolderPath字段即可添加添加来源 Table.AddColumn(展开表内容, 来源文件名, each [Name])然后在报表里可以按文件名做一个简单的“文件行数统计”一目了然地看出有没有漏报。6.3 用PQ自定义函数封装多文件读取流程如果你经常在不同项目里合并文件强烈建议把“读取文件夹并合并”的逻辑封装成一个自定义函数类似这样(路径作为文本) let 源 Folder.Files(路径), 过滤 Table.SelectRows(源, each [Extension] .xlsx and not Text.StartsWith([Name], ~$)), 读取 Table.AddColumn(过滤, 数据, each Excel.Workbook([Content])), 展开 Table.ExpandTableColumn(读取, 数据, {Data}, {数据}), 合并 Table.Combine(展开[数据]) in 合并每个新项目开始时只需要调用这个函数传入新的路径就能快速得到合并表。当然这个函数还需针对Sheet名和列名差异做更严密的处理但作为起点它已经比界面操作高效太多。6.4 合并之前先做数据体检数据体检听着像医疗其实就是花几分钟确认“该读的文件都读到了该清理的问题都被清掉了”。你可以像我一样在Power Query里先把“列名检查”和“行数检查”做成两个独立查询每次合并后先刷新这两个查询确认无误再刷新正式合并表。这种“先看诊断结果、再执行合并”的习惯能让你在数据量变大之后避免很多隐性错误。那些看似偶发的合并失败大多其实在数据准备阶段就已经埋下隐患。7. 经验之谈最值得你记住的几件事7.1 文件规范永远排在技术前面我做了这么多数据分析项目最深的体会是多文件合并的难度通常不在于Power Query函数怎么写而在于数据源本身的质量。文件命名、列名、表格结构、日期格式每一项不规范都会在后期变成报警器。你可能会觉得搞一个“文件规范文档”很老套但只要文件夹里的文件来自多个人、多个系统规范文档就是第一道防线。哪怕你自己今天下的文件三个月后再看也未必能记得当时的格式约定把规范写下来才是对自己负责。7.2 合并只是手段分析才是目的多文件读取合并做完后千万别急着一头扎进报表美化。先确认模型的粒度是否正确再看数据是否经过验证最后才去可视化。一次完整的多文件合并流程应该包含数据准备、Power Query读取、列名与类型统一、验证加载、监控刷新。任何一个环节缺了后续都可能在DAX计算阶段爆发更严重的问题。最后分享一个小技巧在合并结果里增加一列“数据加载时间”用DateTime.LocalNow()生成。这样当你运行定时刷新之后打开报表就知道这次数据是哪一分钟刷进来的配合“来源文件”列能快速判断自己是否拿到了最新版。别小看这个细节关键时刻能省下很多复查时间。如果你手头的Excel文件已经堆成了小山别着急复制粘贴试试今天说的这些方法。先把文件夹整理干净再交给Power Query去读取合并你会发现多文件合并并没有想象中那么头疼。