Power Query 百万级数据处理全攻略:从导入到优化的完整指南

2026-05-09
728
王旭东
数据处理
Power Query 百万级数据处理指南

在数据驱动的时代,处理大规模数据集已经成为数据分析师和财务人员的日常挑战。当你面对一个包含超过一百万行的 CSV 或 Excel 文件时,传统的 Excel 处理方式往往会变得力不从心——文件打开缓慢、内存占用过高、甚至直接崩溃。Power Query 作为微软推出的强大数据处理工具,凭借其强大的数据清洗、转换能力和优秀的性能表现,成为处理百万级数据集的理想选择。本文将为你提供一套完整的方法论,从数据导入到性能优化,帮助你高效处理大规模本地数据文件。

一、为什么选择 Power Query 处理百万级数据?

Power Query 是微软开发的数据获取和转换工具,内置于 Excel 和 Power BI 中。相比传统的数据处理方式,Power Query 在处理大规模数据时具有以下显著优势:

  • 流式处理引擎:Power Query 采用流式处理架构,不需要将整个数据集加载到内存中,大幅降低内存占用
  • 查询折叠(Query Folding):能够智能地将多个转换步骤合并为一个高效操作,减少数据处理时间
  • 延迟计算:在预览阶段仅加载部分数据,直到用户明确加载时才执行完整计算
  • 增量刷新:支持仅加载新增或修改的数据,避免重复处理全量数据
  • M 语言编程:提供强大的公式语言,可以实现复杂的自定义转换逻辑
  • 自动化流程:一次配置,重复使用,大幅提高数据处理效率

二、百万级数据导入:从源头优化性能

1. CSV 文件导入最佳实践

CSV 文件是大数据集最常用的存储格式之一。正确配置导入参数是保证后续处理效率的关键。

步骤一:使用正确的导入方式

在 Excel 中,选择「数据」选项卡 → 「获取数据」→ 「从文件」→ 「从文本/CSV」。不要使用传统的「打开」功能,那会将整个文件加载到内存中。

步骤二:配置导入选项

在导入对话框中,点击「转换数据」进入 Power Query 编辑器,而不是直接「加载」。这一步至关重要,因为:

  • 允许你在加载前进行数据过滤和清洗
  • 可以设置正确的数据类型,避免自动检测导致的错误
  • 减少最终加载到 Excel 的数据量

步骤三:手动指定数据类型

Power Query 默认会进行数据类型自动检测,但对于百万级数据,这个过程会显著拖慢导入速度。建议在导入时禁用自动检测:

// 在高级编辑器中,使用以下代码替代自动类型检测
let
    Source = Csv.Document(
        File.Contents("C:\Data\large_dataset.csv"),
        [Delimiter=",", Columns=15, Encoding=65001, QuoteStyle=QuoteStyle.None]
    ),
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    ManualTypes = Table.TransformColumnTypes(
        PromotedHeaders,
        {
            {"订单编号", type text},
            {"日期", type date},
            {"金额", type number},
            {"数量", Int64.Type},
            {"产品名称", type text}
        }
    )
in
    ManualTypes

提示:使用 Int64.Type 而非 type number 处理整型数据可以节省内存空间。

2. Excel 文件导入策略

处理大型 Excel 文件时,需要特别注意以下几点:

  • 选择特定工作表:避免加载整个工作簿,只选择需要的工作表
  • 指定数据范围:使用命名范围或表来限定导入区域
  • 忽略空白行:设置过滤条件去除空白行,减少不必要的数据处理
// Excel 文件导入优化代码
let
    Source = Excel.Workbook(
        File.Contents("C:\Data\large_excel.xlsx"),
        null,
        true
    ),
    // 选择特定工作表
    SelectedSheet = Source{[Item="SalesData",Kind="Sheet"]}[Data],
    // 提升标题行
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(SelectedSheet),
    // 过滤空白行
    FilteredRows = Table.SelectRows(
        PromotedHeaders,
        each [订单编号] <> null and [订单编号] <> ""
    ),
    // 指定数据类型
    TypedColumns = Table.TransformColumnTypes(
        FilteredRows,
        {
            {"订单编号", type text},
            {"销售日期", type date},
            {"销售金额", type number}
        }
    )
in
    TypedColumns

3. 处理编码和分隔符问题

百万级数据文件中常见的编码问题:

  • UTF-8 编码:使用 Encoding=65001
  • GBK 编码:使用 Encoding=936
  • 自定义分隔符:对于制表符分隔的文件,使用 Delimiter=#(tab)
// 处理 GBK 编码的 CSV 文件
let
    Source = Csv.Document(
        File.Contents("C:\Data\chinese_data.csv"),
        [Delimiter=",", Encoding=936, QuoteStyle=QuoteStyle.Csv]
    )
in
    Source

三、应对性能挑战:百万级数据处理的核心策略

1. 理解 Power Query 的查询引擎机制

要优化百万级数据的处理性能,首先需要理解 Power Query 的工作原理:

  • 延迟执行:Power Query 不会立即执行每个步骤,而是在需要结果时才计算
  • 查询折叠:尽可能将多个转换操作合并为单个数据库查询
  • 分区处理:大型数据集会被分成多个部分并行处理

了解这些机制后,我们可以针对性地优化查询性能。

2. 查询折叠(Query Folding)优化

查询折叠是 Power Query 最重要的性能优化特性之一。当查询折叠发生时,多个转换步骤会被合并为一个更高效的操作。

支持查询折叠的操作:

  • 过滤行(Table.SelectRows)
  • 选择列(Table.SelectColumns)
  • 排序(Table.Sort)
  • 分组(Table.Group)
  • 合并查询(Table.Join / Table.NestedJoin)

如何检查查询折叠是否生效:

在 Power Query 编辑器中,右键点击查询步骤,如果「查看本机代码」选项可用,说明该步骤支持查询折叠。对于本地 CSV/Excel 文件,查询折叠的支持有限,但以下技巧仍然可以提升性能:

// 优化过滤操作顺序 - 尽早过滤不需要的数据
let
    Source = Csv.Document(
        File.Contents("C:\Data\large_dataset.csv"),
        [Delimiter=",", Columns=20, Encoding=65001]
    ),
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    // 第一步就进行过滤,减少后续处理的数据量
    FilteredDate = Table.SelectRows(
        PromotedHeaders,
        each [日期] >= #date(2025, 1, 1)
    ),
    FilteredAmount = Table.SelectRows(
        FilteredDate,
        each [金额] > 0
    ),
    // 过滤后再选择需要的列
    SelectedColumns = Table.SelectColumns(
        FilteredAmount,
        {"订单编号", "日期", "金额", "产品名称"}
    )
in
    SelectedColumns

3. 内存管理技巧

处理百万级数据时,内存管理至关重要。以下是一些实用的内存优化技巧:

技巧一:禁用后台预览

在 Power Query 编辑器中,点击「文件」→「选项和设置」→「查询选项」→「当前工作簿」→「数据加载」,取消勾选「允许后台预览」。这可以显著减少内存占用。

技巧二:使用快速组合

当需要合并多个大型查询时,使用「快速组合」而非逐行合并:

// 使用 Table.Join 进行高效合并
let
    Source1 = Csv.Document(File.Contents("C:\Data\orders.csv"), [Delimiter=","]),
    Orders = Table.PromoteFirstRowAsHeaders(Source1),
    
    Source2 = Csv.Document(File.Contents("C:\Data\customers.csv"), [Delimiter=","]),
    Customers = Table.PromoteFirstRowAsHeaders(Source2),
    
    // 使用 Table.Join 而非逐行查找
    MergedData = Table.Join(
        Orders,
        {"客户ID"},
        Customers,
        {"客户ID"},
        JoinKind.Inner
    )
in
    MergedData

技巧三:避免不必要的列

只保留需要的列,删除无关列可以大幅减少内存占用:

// 尽早删除不需要的列
let
    Source = Csv.Document(
        File.Contents("C:\Data\large_dataset.csv"),
        [Delimiter=",", Columns=50, Encoding=65001]
    ),
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    // 只保留需要的列
    KeepColumns = Table.SelectColumns(
        PromotedHeaders,
        {"订单编号", "日期", "金额", "产品名称", "客户ID"}
    )
in
    KeepColumns

4. 并行处理配置

Power Query 支持并行处理,但需要正确配置才能发挥最大性能:

  • 进入「文件」→「选项和设置」→「查询选项」→「当前工作簿」→「数据加载」
  • 确保「启用并行加载表」已勾选
  • 对于多核处理器,可以设置并行处理的线程数

四、数据清洗技术:高效处理百万行数据

1. 处理缺失值

百万级数据中缺失值是常见问题。以下是高效的缺失值处理方法:

// 高效处理缺失值
let
    Source = Csv.Document(
        File.Contents("C:\Data\large_dataset.csv"),
        [Delimiter=",", Encoding=65001]
    ),
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    
    // 方法一:替换特定列的空值为默认值
    ReplacedNulls = Table.ReplaceValue(
        PromotedHeaders,
        null,
        0,
        Replacer.ReplaceValue,
        {"金额", "数量"}
    ),
    
    // 方法二:填充空值为上一行的值(向前填充)
    FilledForward = Table.FillDown(
        ReplacedNulls,
        {"产品名称", "客户名称"}
    ),
    
    // 方法三:删除包含关键列空值的行
    RemovedNullRows = Table.SelectRows(
        FilledForward,
        each [订单编号] <> null and [金额] <> null
    )
in
    RemovedNullRows

2. 数据类型转换

正确的数据类型转换不仅影响数据质量,还影响处理性能:

// 高效的数据类型转换
let
    Source = Csv.Document(
        File.Contents("C:\Data\large_dataset.csv"),
        [Delimiter=",", Encoding=65001]
    ),
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    
    // 使用 Table.TransformColumnTypes 进行批量类型转换
    TypedColumns = Table.TransformColumnTypes(
        PromotedHeaders,
        {
            {"订单编号", type text},
            {"日期", type date},
            {"创建时间", type datetime},
            {"金额", Currency.Type},
            {"数量", Int64.Type},
            {"是否有效", type logical}
        },
        // 使用可选的 culture 参数处理区域格式
        "zh-CN"
    )
in
    TypedColumns

3. 文本清洗

文本数据清洗是数据预处理的重要环节:

// 文本清洗示例
let
    Source = Csv.Document(
        File.Contents("C:\Data\large_dataset.csv"),
        [Delimiter=",", Encoding=65001]
    ),
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    
    // 添加自定义清洗步骤
    CleanedText = Table.AddColumn(
        PromotedHeaders,
        "清洗后产品名称",
        each 
            // 去除前后空格
            Text.Trim(
                // 转换为大写
                Text.Upper(
                    // 替换特殊字符
                    Text.Replace(
                        Text.Replace([产品名称], "-", " "),
                        "_", " "
                    )
                )
            ),
        type text
    ),
    
    // 使用 Text.Clean 去除不可打印字符
    RemovedSpecialChars = Table.TransformColumns(
        CleanedText,
        {"产品名称", each Text.Clean(_), type text}
    )
in
    RemovedSpecialChars

4. 处理重复数据

百万级数据中的重复数据需要高效处理:

// 高效去重方法
let
    Source = Csv.Document(
        File.Contents("C:\Data\large_dataset.csv"),
        [Delimiter=",", Encoding=65001]
    ),
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    
    // 方法一:基于单个列去重,保留第一条记录
    DeduplicatedSingle = Table.Distinct(
        PromotedHeaders,
        {"订单编号"}
    ),
    
    // 方法二:基于多个列组合去重
    DeduplicatedMulti = Table.Distinct(
        PromotedHeaders,
        {"订单编号", "日期"}
    ),
    
    // 方法三:保留重复项中标金额最高的记录
    GroupedMax = Table.Group(
        PromotedHeaders,
        {"订单编号"},
        {
            {"最高金额", each List.Max([金额]), type number},
            {"完整记录", each _, type table}
        }
    ),
    ExpandedRecords = Table.ExpandTableColumn(
        GroupedMax,
        "完整记录",
        {"日期", "产品名称", "数量"},
        {"日期", "产品名称", "数量"}
    )
in
    ExpandedRecords

五、数据转换技巧:让百万级数据发挥价值

1. 条件列创建

根据业务逻辑创建条件列是常见的数据转换需求:

// 高效创建条件列
let
    Source = Csv.Document(
        File.Contents("C:\Data\large_dataset.csv"),
        [Delimiter=",", Encoding=65001]
    ),
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    
    // 使用 Table.AddColumn 创建条件列
    AddedCategory = Table.AddColumn(
        PromotedHeaders,
        "金额区间",
        each 
            if [金额] >= 10000 then "大额"
            else if [金额] >= 1000 then "中额"
            else if [金额] >= 100 then "小额"
            else "微额",
        type text
    ),
    
    // 添加日期相关列
    AddedDateParts = Table.AddColumn(
        AddedCategory,
        "年份",
        each Date.Year([日期]),
        Int64.Type
    ),
    
    AddedMonth = Table.AddColumn(
        AddedDateParts,
        "月份",
        each Date.Month([日期]),
        Int64.Type
    )
in
    AddedMonth

2. 数据分组与聚合

百万级数据的分组聚合需要特别注意性能:

// 高效的数据分组与聚合
let
    Source = Csv.Document(
        File.Contents("C:\Data\large_dataset.csv"),
        [Delimiter=",", Encoding=65001]
    ),
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    
    // 基本分组聚合
    GroupedData = Table.Group(
        PromotedHeaders,
        {"产品名称", "年份", "月份"},
        {
            {"总销售额", each List.Sum([金额]), type number},
            {"平均单价", each List.Average([金额]), type number},
            {"订单数量", each List.Count([订单编号]), Int64.Type},
            {"最大单笔金额", each List.Max([金额]), type number},
            {"最小单笔金额", each List.Min([金额]), type number}
        }
    ),
    
    // 排序结果
    SortedData = Table.Sort(
        GroupedData,
        {{"总销售额", Order.Descending}}
    )
in
    SortedData

3. 数据透视与逆透视

数据透视和逆透视是重塑数据结构的重要工具:

// 数据逆透视(将宽表转换为长表)
let
    Source = Excel.Workbook(
        File.Contents("C:\Data\wide_data.xlsx"),
        null,
        true
    ),
    SelectedSheet = Source{[Item="Data",Kind="Sheet"]}[Data],
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(SelectedSheet),
    
    // 逆透视除固定列外的所有列
    UnpivotedData = Table.UnpivotOtherColumns(
        PromotedHeaders,
        {"订单编号", "日期", "客户名称"},
        "月份",
        "销售额"
    )
in
    UnpivotedData

// 数据透视(将长表转换为宽表)
let
    Source = Csv.Document(
        File.Contents("C:\Data\long_data.csv"),
        [Delimiter=",", Encoding=65001]
    ),
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    
    // 按产品名称透视月份数据
    PivotedData = Table.Pivot(
        PromotedHeaders,
        List.Distinct(PromotedHeaders[月份]),
        "月份",
        "销售额",
        List.Sum
    )
in
    PivotedData

4. 合并多个数据源

处理来自多个文件的数据是常见场景:

// 合并文件夹中的所有 CSV 文件
let
    // 获取文件夹中的所有文件
    Source = Folder.Files("C:\Data\SalesFiles\"),
    
    // 过滤只保留 CSV 文件
    FilteredCSV = Table.SelectRows(
        Source,
        each Text.EndsWith([Extension], ".csv")
    ),
    
    // 只保留需要的列
    KeptColumns = Table.SelectColumns(
        FilteredCSV,
        {"Content", "Name", "Date modified"}
    ),
    
    // 定义解析单个 CSV 文件的函数
    ParseCSV = (binaryContent) =>
        let
            CsvContent = Csv.Document(
                binaryContent,
                [Delimiter=",", Encoding=65001]
            ),
            Promoted = Table.PromoteFirstRowAsHeaders(CsvContent),
            Typed = Table.TransformColumnTypes(
                Promoted,
                {
                    {"订单编号", type text},
                    {"日期", type date},
                    {"金额", type number}
                }
            )
        in
            Typed,
    
    // 应用函数到每个文件
    AddedContent = Table.AddColumn(
        KeptColumns,
        "Data",
        each ParseCSV([Content])
    ),
    
    // 展开所有数据
    ExpandedData = Table.ExpandTableColumn(
        AddedContent,
        "Data",
        {"订单编号", "日期", "金额"},
        {"订单编号", "日期", "金额"}
    ),
    
    // 添加文件名作为来源标识
    AddedSource = Table.AddColumn(
        ExpandedData,
        "来源文件",
        each Text.BeforeDelimiter([Name], ".csv"),
        type text
    ),
    
    // 删除原始 Content 列
    RemovedContent = Table.RemoveColumns(
        AddedSource,
        {"Content"}
    )
in
    RemovedContent

六、M 语言进阶:自定义函数与高级转换

1. 创建自定义函数

自定义函数可以大幅提高复杂转换的重用性和效率:

// 创建金额格式化函数
let
    FormatAmount = (amount as number) as text =>
        let
            Formatted = Text.From(
                Number.Round(amount, 2),
                "zh-CN"
            ),
            WithComma = Number.ToText(amount, "#,##0.00")
        in
            WithComma
in
    FormatAmount

// 使用自定义函数
let
    Source = Csv.Document(
        File.Contents("C:\Data\large_dataset.csv"),
        [Delimiter=",", Encoding=65001]
    ),
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    
    // 应用自定义函数
    AddedFormatted = Table.AddColumn(
        PromotedHeaders,
        "格式化金额",
        each FormatAmount([金额]),
        type text
    )
in
    AddedFormatted

2. 递归函数处理层次数据

处理具有层次结构的数据时,递归函数非常有用:

// 递归函数示例:计算累计值
let
    CalculateCumulative = (table as table, column as text) as table =>
        let
            Sorted = Table.Sort(table, {{"日期", Order.Ascending}}),
            AddedIndex = Table.AddIndexColumn(Sorted, "Index", 0, 1),
            AddedCumulative = Table.AddColumn(
                AddedIndex,
                "累计" & column,
                (row) => List.Sum(
                    List.FirstN(
                        Table.Column(AddedIndex, column),
                        row[Index] + 1
                    )
                ),
                type number
            ),
            RemovedIndex = Table.RemoveColumns(AddedCumulative, {"Index"})
        in
            RemovedIndex
in
    CalculateCumulative

3. 错误处理机制

百万级数据中难免会有错误数据,正确的错误处理可以避免整个查询失败:

// 错误处理示例
let
    Source = Csv.Document(
        File.Contents("C:\Data\large_dataset.csv"),
        [Delimiter=",", Encoding=65001]
    ),
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    
    // 使用 try...otherwise 处理可能的错误
    SafeConversion = Table.AddColumn(
        PromotedHeaders,
        "安全金额",
        each 
            try 
                Number.From([金额])
            otherwise 
                0,
        type number
    ),
    
    // 或者使用 Table.ReplaceErrorValues
    ReplacedErrors = Table.ReplaceErrorValues(
        SafeConversion,
        {{"安全金额", 0}}
    )
in
    ReplacedErrors

七、工作流优化:提升百万级数据处理效率

1. 查询依赖关系优化

合理规划查询之间的依赖关系可以显著提高性能:

  • 避免循环依赖:确保查询之间的引用形成单向依赖链
  • 使用引用查询:对于多个查询需要的中间结果,创建引用查询而非重复处理
  • 禁用不必要的查询加载:在查询属性中取消勾选「启用加载」,避免加载中间结果

2. 数据加载选项配置

正确的数据加载选项可以大幅提升性能:

  • 仅创建连接:如果不需要在 Excel 中显示数据,选择「仅创建连接」
  • 加载到数据模型:对于百万级数据,加载到 Power Pivot 数据模型而非工作表
  • 使用增量刷新:对于定期更新的数据源,配置增量刷新策略

3. 诊断与性能分析

使用 Power Query 内置的诊断工具识别性能瓶颈:

  • 点击「查看」→「显示诊断步骤」,查看每个步骤的执行时间
  • 使用「查看本机代码」检查查询折叠是否生效
  • 通过「性能分析」窗口识别最耗时的步骤

八、故障排除:百万级数据处理的常见问题与解决方案

1. 内存不足错误

问题描述:处理百万级数据时出现「内存不足」或「Out of Memory」错误。

解决方案:

  • 启用 64 位版本的 Excel 或 Power BI
  • 禁用后台预览(如前所述)
  • 将数据加载到数据模型而非工作表
  • 尽早过滤不需要的行和列
  • 使用更高效的数据类型(如 Int64.Type 代替 type number)

2. 查询执行缓慢

问题描述:查询执行时间过长,影响工作效率。

解决方案:

  • 检查查询折叠是否生效
  • 优化过滤条件的顺序,尽早过滤数据
  • 避免使用复杂的自定义函数
  • 拆分大型查询为多个小型查询
  • 使用「快速组合」替代逐行操作

3. 数据类型转换错误

问题描述:数据类型转换时出现错误,部分数据无法正确转换。

解决方案:

  • 使用 try...otherwise 处理异常值
  • 在转换前清洗数据(如去除空格、特殊字符)
  • 使用 Text.Clean 清理不可打印字符
  • 检查区域设置是否正确(使用 culture 参数)

4. 文件编码问题

问题描述:导入文件时出现乱码或字符显示不正确。

解决方案:

  • 确认文件编码格式(UTF-8、GBK 等)
  • 在 Csv.Document 中指定正确的 Encoding 参数
  • 使用 Notepad++ 等工具转换文件编码后重新导入

九、实战案例:完整的数据处理流程

案例背景

某零售企业需要处理包含 150 万行销售数据的 CSV 文件,该文件来自多个门店,需要进行数据清洗、转换和汇总分析。

完整代码实现

// 完整的百万级销售数据处理流程
let
    // 第一步:导入数据
    Source = Csv.Document(
        File.Contents("C:\Data\sales_2025.csv"),
        [Delimiter=",", Columns=12, Encoding=65001, QuoteStyle=QuoteStyle.Csv]
    ),
    
    // 第二步:提升标题行
    PromotedHeaders = Table.PromoteFirstRowAsHeaders(Source),
    
    // 第三步:尽早过滤无效数据
    FilteredValid = Table.SelectRows(
        PromotedHeaders,
        each 
            [订单编号] <> null and 
            [订单编号] <> "" and
            [销售金额] <> null and
            [销售金额] > 0
    ),
    
    // 第四步:选择需要的列
    SelectedColumns = Table.SelectColumns(
        FilteredValid,
        {"订单编号", "销售日期", "门店编号", "产品名称", "销售金额", "销售数量"}
    ),
    
    // 第五步:数据类型转换
    TypedColumns = Table.TransformColumnTypes(
        SelectedColumns,
        {
            {"订单编号", type text},
            {"销售日期", type date},
            {"门店编号", type text},
            {"产品名称", type text},
            {"销售金额", Currency.Type},
            {"销售数量", Int64.Type}
        }
    ),
    
    // 第六步:文本清洗
    CleanedText = Table.TransformColumns(
        TypedColumns,
        {
            {"产品名称", each Text.Clean(Text.Trim(_)), type text},
            {"门店编号", each Text.Trim(_), type text}
        }
    ),
    
    // 第七步:添加计算列
    AddedYear = Table.AddColumn(
        CleanedText,
        "年份",
        each Date.Year([销售日期]),
        Int64.Type
    ),
    
    AddedMonth = Table.AddColumn(
        AddedYear,
        "月份",
        each Date.Month([销售日期]),
        Int64.Type
    ),
    
    AddedQuarter = Table.AddColumn(
        AddedMonth,
        "季度",
        each "Q" & Text.From(Date.QuarterOfYear([销售日期])),
        type text
    ),
    
    // 第八步:创建分类列
    AddedCategory = Table.AddColumn(
        AddedQuarter,
        "金额区间",
        each 
            if [销售金额] >= 5000 then "大额订单"
            else if [销售金额] >= 1000 then "中额订单"
            else if [销售金额] >= 100 then "小额订单"
            else "微额订单",
        type text
    ),
    
    // 第九步:去重
    Deduplicated = Table.Distinct(
        AddedCategory,
        {"订单编号", "产品名称"}
    ),
    
    // 第十步:创建汇总表
    SummaryTable = Table.Group(
        Deduplicated,
        {"年份", "月份", "季度", "门店编号"},
        {
            {"总销售额", each List.Sum([销售金额]), Currency.Type},
            {"总销售数量", each List.Sum([销售数量]), Int64.Type},
            {"订单数量", each List.Count([订单编号]), Int64.Type},
            {"平均订单金额", each List.Average([销售金额]), Currency.Type}
        }
    ),
    
    // 第十一步:排序
    SortedSummary = Table.Sort(
        SummaryTable,
        {
            {"年份", Order.Descending},
            {"月份", Order.Descending},
            {"总销售额", Order.Descending}
        }
    )
in
    SortedSummary

处理效果

  • 处理时间:从原始处理时间约 15 分钟优化到 3 分钟以内
  • 内存占用:从超过 2GB 降低到约 500MB
  • 数据质量:清理了约 2.3% 的无效数据
  • 输出结果:生成了按月、门店的汇总分析表

十、最佳实践总结

1. 数据导入阶段

  • 优先使用 CSV 格式而非 Excel 格式存储大数据
  • 手动指定数据类型,避免自动检测
  • 正确配置编码和分隔符参数
  • 使用「转换数据」而非直接「加载」

2. 数据清洗阶段

  • 尽早过滤不需要的行和列
  • 使用批量操作而非逐行处理
  • 合理使用 try...otherwise 处理异常值
  • 使用高效的去重方法

3. 数据转换阶段

  • 利用查询折叠优化转换操作
  • 避免在大型数据集上使用复杂的自定义函数
  • 合理使用分组和聚合功能
  • 优先使用内置函数而非自定义逻辑

4. 性能优化阶段

  • 禁用不必要的查询加载
  • 启用并行处理和后台刷新
  • 使用诊断工具识别性能瓶颈
  • 对于定期更新的数据,配置增量刷新

十一、总结与展望

Power Query 为处理百万级本地 CSV 和 Excel 文件提供了一套完整的解决方案。通过合理配置导入选项、优化查询结构、使用高效的清洗和转换技术,我们可以在不借助专业数据库的情况下,高效处理大规模数据集。

关键在于理解 Power Query 的工作原理,特别是查询折叠、延迟计算和并行处理等核心机制。掌握这些原理后,结合本文介绍的最佳实践和故障排除技巧,你将能够轻松应对各种大规模数据处理挑战。

随着数据量的持续增长,建议逐步学习和掌握 Power Query 的高级功能,如自定义函数、错误处理、增量刷新等。同时,对于超大规模数据(千万级或亿级),可能需要考虑使用专业的数据库或大数据处理平台,但 Power Query 仍然是数据预处理和探索性分析的优秀工具。

希望本文能为你的数据处理工作带来实质性的帮助,让你的百万级数据处理工作变得更加高效和轻松!

王旭东

王旭东

资深数据分析师 | 业财融合专家

拥有11年财务分析经验,专注于业财融合、数据可视化在企业财务中的应用。