Lazy loaded imagePower Query功能详解之追加查询

type
status
date
slug
summary
tags
category
icon
password
notion image

PowerQuery功能详解之追加查询

“追加查询”这个功能在不同数据布局场景下有不同的应用。追加查询的威力,并不仅仅局限于处理Excel文件。下面,我为您整理一份关于“追加查询”功能的、更完整的应用场景和原理概要。

一、“追加查询”功能完整应用场景

核心思想:将多个来源、但结构相似的数据,垂直地、像叠罗汉一样堆叠在一起,形成一个行数更多的、统一的数据集。
普通职场人最常用的三种核心场景是:

1. 同一工作簿下的多个工作表

  • 场景描述:您有一个Excel报告文件,里面包含了一月、二月、三月...等多个工作表,您需要将它们汇总成一张年度总表。
  • 实现方式:通过 数据 -> 获取数据 -> 从文件 -> 从工作簿,连接到当前这个工作簿本身。Power Query会列出其中所有的工作表对象,然后您可以将它们全部选中并追加。
  • 关键技巧:下面我们会深入探讨,为了让这个查询在文件移动后依然有效,最佳实践是使用动态路径Excel.CurrentWorkbook())的方式来实现。

2. 不同工作簿下的多个工作表

  • 场景描述:您从不同的人或部门那里,收到了多个独立的Excel文件,比如华东区销售.xlsx、华北区销售.xlsx、华南区销售.xlsx。它们的内部结构完全一样,您需要将它们汇总成全国销售总表。
  • 实现方式
      1. 在Power Query中,为每一个Excel文件分别创建一个独立的数据源连接查询(查询_华东, 查询_华北, 查询_华南)。
      1. 然后,使用 “将查询追加为新查询” 功能,将这几个独立的查询,合并成一个全新的、总的查询(全国销售总表)。

3. 同一文件夹下的所有文件 - 这是最强大的用法!

  • 场景描述:您有一个专门的文件夹,比如“月度销售报告”。每个月,您都会把最新月份的、格式完全一样的Excel或CSV报告文件扔进这个文件夹。您希望有一个报表,能自动地把这个文件夹里所有的报告都汇总起来。
  • 实现方式:使用 数据 -> 获取数据 -> 从文件 -> 从文件夹
  • 神奇之处
    • 您只需要设置一次查询。Power Query会创建一个“示例文件转换”的函数。
    • 未来,当您向这个文件夹中添加新的文件(比如“四月销售.xlsx”)时,您不需要做任何修改!您只需要在最终的报表中点击一次“刷新”,Power Query会自动发现新文件,并将其内容追加到总表中。
    • 同样,如果您删除了更新了文件夹里的某个文件,刷新后结果也会自动同步。

Power Query的追加功能远不止于Excel文件。它的强大之处在于,它可以追加任何来源、只要结构能被统一的数据。

4. 不同类型的多个文件

  • 场景描述:您的销售数据一部分记录在Excel文件里,另一部分记录在CSV文件里,还有一部分记录在文本文档里。但它们的列结构是相似的。
  • 实现方式:您可以先分别建立对Excel和CSV的查询,在Power Query内部将它们的列名和数据类型清洗成完全一致的结构,然后再将这两个查询追加在一起。

5. 数据库中的多个表

  • 场景描述:在公司的SQL数据库中,由于性能或历史原因,销售数据被分成了Sales_2022、Sales_2023、Sales_2024等多张表。您需要分析近三年的总销售情况。
  • 实现方式:在Power Query中,分别连接到这三张数据库表,然后将它们追加成一个查询。

6. 网页或API的分页数据

  • 场景描述:您需要从一个网站或API接口获取数据,但这个接口为了防止服务器过载,每次只返回100条记录(即“分页”)。您需要获取全部的几千条数据。
  • 实现方式:您可以创建一个函数来获取指定页码的数据,然后调用这个函数获取第1页、第2页、第3页...的数据,并将所有返回的查询结果追加在一起,形成完整的数据集。

总结:追加查询的应用场景

场景
核心描述
解决的典型问题
同一工作簿
将一个文件内部的多个Sheet/Table垂直合并。
汇总月度、季度报表;合并不同业务分类的子表。
不同工作簿
将多个独立的、结构相同的Excel文件垂直合并。
汇总来自不同分公司、不同部门的同类报告。
文件夹
(最强自动化) 自动合并一个文件夹内所有符合条件的文件。
一劳永逸地处理定期新增的报告文件(如月报、日报)。
不同文件类型
将Excel, CSV, TXT等不同类型但结构相似的文件合并。
整合历史遗留的、格式不统一的数据源。
数据库表
将数据库中结构相同的多个分表(如按年分的表)合并。
进行跨时间周期的大范围数据分析。
Web/API分页
将网页或API返回的多个数据页面合并。
爬取和获取完整的线上数据集。
所以,追加查询的本质是“垂直堆叠结构相似的数据”,那么无论数据来自何方,您都能灵活地运用这个强大的功能。

二、同一工作簿下的多个工作表的追加查询

这种情况有3个核心关键需要搞清楚
  • ① 逻辑健壮性:通过筛选防止查询的循环引用
  • ② 路径健壮性:通过动态路径实现查询的可移植性
  • ③ 结构健壮性:通过动态列名实现查询的可扩展性

以下视频为以上3项内容的完整讲解:

本视频教案下载地:

以下为文字版和相关的代码资源:


主题①:逻辑健壮性 — 通过筛选防止查询的循环引用

1. 痛点呈现

在我们使用Power Query处理“数据源与结果在同一工作簿”的场景时,一个极其危险的“幽灵”常常不请自来。
现象:您第一次刷新查询,得到了6行正确的汇总数据。但是,当您第二次、第三次刷新时,数据量会莫名其妙地变成12行、24行,甚至更多。每一次刷新,都会让数据成倍增长,最终导致整个报表的数据完全错误,我们称之为“数据爆炸”“循环引用”。
复现场景: 假设我们已经通过动态路径连接到了当前工作簿,并展开了所有工作表的数据。此时,我们的数据看起来像这样(简化版):
Name
员工姓名
销售额
Q1_Sales
张三
5000
Q1_Sales
李四
3500
Q2_Sales
王五
8000
...
...
...
追加1
张三
5000
追加1
李四
3500
追加1
王五
8000
您会发现,Power Query不仅读取了原始的Q1_SalesQ2_Sales等工作表,还把它自己上次加载回去的那个结果表(这里假设名为追加1)也当作了新的源数据,一起给合并进来了。

2. 原因剖析

这个问题的根源在于我们使用的Excel.Workbook()函数。
  • Excel.Workbook()的工作机制:这个函数会忠实地扫描并列出指定Excel文件中所有的对象,包括所有的工作表(Sheets)、已定义的表格(Tables)和已定义名称(Defined Names)。
  • 循环的产生
      1. 我们的查询指令是:“请读取当前这个文件里所有的工作表。”
      1. 第一次运行时,它正确地读取了Q1_Sales, Q2_Sales... 并将结果加载到了一个新的工作表,比如追加1.
      1. 第二次刷新时,Power Query再次执行第一条指令。但这一次,当它扫描“当前这个文件”时,它发现工作表的列表里,除了原始的Q1_Sales等,还多了一个我们上次创建的追加1
      1. 由于我们没有告诉它要排除追加1,它便忠实地将追加1也作为原材料,和Q1_Sales等一起进行了合并。
      1. 这就形成了一个恶性循环:查询的结果,成为了下一次查询的源头之一。

3. 解决方案

解决方案的核心思想非常直接:我们必须在数据处理的早期阶段,就通过“筛选”操作,明确地告诉Power Query,哪些是“原材料”,哪些是我们自己创造的“成品”,绝对不能混淆。
操作步骤:
  1. 定位到关键步骤:在Power Query编辑器的“应用的步骤”列表中,找到并选中“源 (Source)”这一步(或者是紧接着它的某一步,只要能看到所有工作表/表格的列表即可)。
  1. 应用筛选规则
      • 此时,您的预览窗口应该有一个名为Name的列,其中列出了Q1_Sales, Q2_Sales, Config, 追加1等所有对象的名称。
      • 点击 Name 列的筛选下拉箭头
      • 在弹出的列表中,取消勾选那个代表您最终汇总结果的工作表名称(追加1),以及其他任何不属于原始数据的工作表(如Config)。
      • 点击“确定”。
  1. 代码层面的变化: 可以直接忽略,参考界面操作步骤即可。
最终效果: 通过添加这一步筛选,我们的查询“配方”里就有了一条明确的指令:“在进行任何合并操作之前,请永远忽略名为追加1Config这一类的非源数据工作表。” 这样,无论我们刷新多少次,Power Query都只会处理我们指定的、真正的原始数据,循环被彻底打破,查询的逻辑健壮性得到了保证。

主题②:路径健壮性 — 通过动态路径实现查询的可移植性

1. 痛点呈现

我们构建自动化报表的初衷,是为了“一劳永逸”。但很多初学者很快就会发现一个令人沮丧的事实:这个“一劳永逸”是有条件的,条件就是——“文件永远不能动!”
现象:您精心制作了一个Power Query自动化报表,它在您的电脑上完美运行。但是,当您遇到以下任何一种情况时,灾难就会发生:
  • 您为了整理桌面,把这个Excel文件从桌面移动到了D:\\我的工作\\报告文件夹。
  • 您将这个Excel文件通过邮件发送给您的同事
  • 您将整个项目文件夹上传到了共享网络驱动器。
在以上任何一种情况下,当您或您的同事试图点击“刷新”时,Power Query会立刻“翻脸不认人”,弹出一个冰冷的错误提示: DataSource.Error: Could not find file 'C:\\Users\\你的名字\\Desktop\\原始数据.xlsx'.
结果:整个自动化流程在第一步就宣告失败。您不得不进入每一个查询,手动修改文件路径,极大地削减了自动化的价值,也让文件协作变得异常困难。

2. 原因剖析

这个问题的根源在于,标准的“获取数据”方法,在Power Query中记录的是一个写死的、绝对的路径
  • 绝对路径 (Absolute Path):这是一个从电脑的“根目录”(如C:\\)开始的、完整的、独一无二的文件地址。它就像一个完整的家庭住址,包含了国家、省、市、街道、门牌号。
  • Power Query的默认行为:当您通过 获取数据 -> 从文件 连接到一个Excel工作簿时,Power Query的“源”步骤会忠实地记录下这个绝对路径。
    • 其M语言代码如下所示,路径部分是一个纯文本字符串
  • 问题所在:这个“地址”是硬编码 (Hard-coded) 的。Power Query就像一个只会按固定地址送信的“笨邮差”,它不知道文件已经“搬家”了。当文件位置发生任何变化,这个写死的地址就变成了一个无效地址,导致“查无此件”。

3. 解决方案

解决方案的核心思想,是把Power Query从一个“笨邮差”升级为一个“智能侦探”。我们不再给它一个写死的地址,而是赋予它一种“随时找到自己在哪”的能力
这需要Excel和Power Query的协同作战。
第一步:在Excel中创建一个“动态路径信标”
我们首先需要在Excel文件内部,创建一个能实时报告自己位置的“信标”。
  1. 新建配置工作表:在您的Excel工作簿中,新建一个工作表,命名为Config(或“路径”等您自己能看懂的名字)。
  1. 使用CELL函数:在Config工作表的任意单元格(比如A10),输入这个Excel函数: =CELL("filename", A10) 这个函数会立即返回当前工作簿的完整文件路径,例如:C:\\我的工作\\报告\\[我的分析文件.xlsx]Config。这个路径是动态的,您把文件移到哪里,它就会自动更新成新的路径。但是他是取到工作表级别的路径的,我们只需要取到工作簿路径即可。按下面的函数继续取数。=SUBSTITUTE(LEFT(CELL("filename",A10),FIND("]",CELL("filename",A10))-1),"[","")
  1. 定义名称:选中A10单元格,点击顶部 “公式” -> “定义名称”。在“名称”输入框中,给它起一个简洁、有意义的名字,比如 工作簿路径。点击“确定”。
    1. (现在,我们就在Excel内部,创建了一个名为工作簿路径的、随时可以引用的、代表当前文件路径的“全局变量”。)
第二步:在Power Query中引用这个“信标”
现在,我们回到Power Query,改造我们的“源”步骤。
  1. 定位到“源”步骤:进入Power Query编辑器,选中需要修改的查询,并点击第一步“源”。
  1. 修改M代码:看向顶部的公式栏,找到那段包含绝对路径的代码。我们的目标,是用一段Excel.CurrentWorkbook(){[Name="工作簿路径"]}[Content]{0}[Column1]能够读取Excel已定义名称的M代码,来替换掉那个写死的路径字符串
    1. 修改前:
      修改后 (针对数据源就是当前文件本身):
      • 代码解读(核心部分):
        • Excel.CurrentWorkbook(){[Name="工作簿路径"]}[Content]{0}[Column1]:这段代码的作用是:“在当前这个Excel工作簿里,找到名叫工作簿路径的对象,并把它的值(那个长长的路径文本)取出来。”
最终效果: 经过改造后,“源”步骤的指令从“去这个写死的地址找文件”,变成了“先问问Excel里的工作簿路径信标,当前文件在哪里,然后再去那个地址找文件”。
这样,无论您将这个Excel文件移动到任何地方,或者发给任何人,只要他一打开文件,CELL函数就会自动更新路径,“信标”就会指向正确的位置。当他点击“刷新”时,Power Query就会根据“信标”的最新指示,准确无误地找到数据源,从而实现了查询的完全可移植性路径健壮性

主题③:结构健壮性 — 通过动态列名实现查询的可扩展性

1. 痛点呈现

我们已经构建了一个逻辑正确、路径稳健的查询。它现在看起来似乎“完美”了。但是,一个更隐蔽的“定时炸弹”已经埋下。
现象:您的自动化报表已经稳定运行了好几个月。突然有一天,业务部门通知您:“从这个季度开始,我们的源数据表中增加了一个新的指标列,比如‘销售成本’,请把它也加到您的汇总报表里。”
您自信满满地把包含了新列的源数据文件放到指定位置,然后回到您的Power Query报表,点击“刷新”。刷新过程很顺利,没有报任何错误。但是,当您检查结果时,却沮-丧地发现——那个新的“销售成本”列,根本没有出现!
结果:您的自动化流程“选择性失明”了。它无法自动适应源数据结构的变化,您不得不再次进入Power Query编辑器,手动修改查询,这让“一劳永逸”的承诺大打折扣。

2. 原因剖析

这个问题的根源在于Power Query中 “展开列 (Expand Column)” 这个步骤的默认行为
  • “展开”操作的背后:当您使用 Excel.Workbook() 函数获取数据后,会得到一个包含[Data]列的元数据表,其中每一行的[Data]都是一个嵌套的表格。为了将它们合并,您会点击[Data]列的展开按钮。
notion image
  • 默认行为:写死列名:在您点击展开按钮时,Power Query会弹出一个菜单,让您选择要展开的列。当您点击“确定”后,Power Query会在后台生成一步M代码。这个代码会将您当前所选的所有列的名称,以一个固定的文本列表形式,硬编码 (Hard-coded) 在步骤中。
    • 这个M代码通常看起来像这样:
  • 问题所在:这个步骤的“配方”被明确地定义为:“请展开Data列,并且提取出名为员工姓名, 销售区域, 产品ID, 销售额的这几列。”
  • “定时炸弹”引爆:当您的源数据增加了新的“销售成本”列后,刷新时,Power Query引擎依然会严格按照这份“旧配方”去执行。由于“销售成本”这个名字不在那份写死的列表中,所以它就被无情地、静默地忽略了

3. 解决方案

解决方案的核心思想,是把那份“写死的列名清单”,替换成一个“动态生成的、能自动更新的列名清单”。我们不再告诉Power Query具体要展开哪些列,而是赋予它一种能力,让它在每次刷新时,自己去侦察汇总所有应该被展开的列。
这需要我们手动介入,用M语言构建一个动态列表。
操作步骤:
  1. 定位到关键步骤之前:在Power Query编辑器的“应用的步骤”列表中,找到并选中“展开列”(Expanded Data或类似名称)这一步的“上一步”。通常是您筛选完需要的工作表之后的那一步(比如在删除的其他列之后)。
      • 此时,您的预览窗口应该是一个元数据表,[Data]列里包含了所有需要被合并的嵌套表格。
  1. 插入步骤,创建动态列名列表
      • 点击公式栏左侧的 fx 按钮,插入一个新步骤。
      • 将公式栏中的内容,替换为下面这段“魔法代码”:
      • 代码解读
          1. List.Transform(..., each Table.ColumnNames(_)): 遍历每一个嵌套表([Data]),并提取出它们各自的列名列表。
          1. List.Combine(...): 将所有这些单独的列名列表,合并成一个包含所有列名(有重复)的大列表。
          1. List.Distinct(...): 对这个大列表进行去重,得到一个最终的、包含了所有源表中出现过的全部唯一列名的“超级列表”。
      • 按下回车后,这个步骤的结果就是那个动态生成的列名列表。将这个步骤重命名为一个有意义的名字,比如 列标题,是动态变动的。
  1. 改造“展开列”步骤
      • 现在,重新选中您原来的“展开列”那一步。
      • 看向顶部的公式栏,找到那段写死的列名列表 {"员工姓名", "销售区域", ...}
      • 执行关键替换:将这个写死的列表,直接替换为您上一步创建的动态列表的名称 列标题
      修改前:
      修改后:
最终效果: 经过改造后,整个查询的自动化流程变得极具弹性可扩展性
  1. 刷新时,Power Query首先会执行列标题这一步,它会重新扫描所有最新的源数据,并生成一份包含了所有新旧列名的最新“超级列表”。
  1. 紧接着,当执行“展开列”步骤时,它不再依赖于一份过时的“旧清单”,而是直接使用刚刚生成的列标题这个最新的、最全的清单来展开数据。
这样,无论未来您的源数据表增加、减少或修改了任何列,您的Power Query查询都能够自动适应这些结构上的变化,确保所有数据都能被准确无误地包含进来,从而实现了真正的“面向未来”的结构健壮性。

二、同一工作簿下的多个工作表的追加查询

(待续)
 
 

三、同一文件夹下的所有文件 - 这是最强大的用法!

(待续)
 
 
 

如果想深入学习,可以付费¥129,加入我的知识星球,加我的微信,一对一教学。

notion image
notion image
 
 
 
上一篇
FIND()、LEN()、Right()函数综合使用,高效自由单元格部分数据
下一篇
财务BP岗位角色深度调查分析(价值百万)
Loading...
文章列表
让财税成为经营的力量
AI赋能高效学习PowerQuery
知识运用
实战教程
CPA考试学习笔记
Excel使用技巧
人工智能追踪
案例解析
财税政策
中小企业财税实务必修课
学习小记