Google Sheets QUERY 函数基于 Google Visualization API 查询语言(GQL),支持八种子句:SELECT、WHERE、GROUP BY、PIVOT、ORDER BY、LIMIT、OFFSET 和 LABEL。但它不支持——而这也是大多数分析师踩坑的地方——子查询、窗口函数、跨区域 JOIN 以及可靠的 NULL 处理。这四个短板在实际财务模型中会直接导致功能失效,值得在构建依赖此函数的体系之前充分了解。
QUERY 函数支持的功能
完整子句集的文档可参阅 Google Visualization API 查询语言参考文档。截至 2026 年 4 月,支持范围如下:
| 子句 | 功能说明 | 跨标签页是否可用 |
|---|---|---|
SELECT | 选择并重新排列列 | 是 |
WHERE | 按条件筛选行 | 是 |
GROUP BY | 配合 SUM、AVG、COUNT、MAX、MIN 聚合 | 是 |
PIVOT | 交叉制表 | 是,但有注意事项 |
ORDER BY | 升序或降序排列 | 是 |
LIMIT | 限制输出行数 | 是 |
OFFSET | 跳过指定行数 | 是 |
LABEL | 重命名输出列标题 | 是 |
一个典型的 FP&A 应用场景:从逾 2 万行的总账明细中提取所有金额超过 35 万元的交易,并按成本中心汇总——无需辅助列,无需中间区域。
=QUERY('总账明细'!A:F,
"SELECT B, SUM(F)
WHERE F > 350000
GROUP BY B
ORDER BY SUM(F) DESC
LABEL SUM(F) '合计支出'",
1)
跨标签页引用完全可行——'总账明细'!A:F 是标准写法。函数内部如何将列识别为 Col1、Col2 等,还是直接使用字母列引用,取决于调用方式,详见另一篇关于 QUERY Col1 Col2 语法 的文章。
PIVOT 子句值得单独说明。它能将平面交易表转换为类似利润表的矩阵结构,而无需使用数据透视表,实用价值较高。但有一个前提:枢轴列的值必须在写公式时已知,且输出列会动态生成——这意味着一旦数据发生变化,按位置引用特定列的下游公式将静默失效。在制作董事会汇报材料时,这一点尤为关键。
QUERY 函数不支持的功能
对于构建多标签页模型的分析师而言,这部分内容更为重要。GQL 规范文档 明确了以下排除项:
子查询。 无法在 QUERY 内部嵌套另一个 QUERY。如果需要基于聚合结果进行筛选——例如只保留合计支出超过 700 万元的成本中心——则需要两步操作:先将第一个 QUERY 的结果输出至辅助区域,再对该区域执行第二个 QUERY。略显繁琐,但可行。
窗口函数。 不支持 ROW_NUMBER()、RANK(),也不支持通过 OVER PARTITION BY 实现的滚动汇总。若需要按 SKU 进行贡献边际排名分析,要么将数据引出至辅助列,要么接受 QUERY 无法原生完成此类任务的现实。
跨区域 JOIN。 QUERY 只能操作单一连续区域,无法在一次调用中将 '利润表'!A:D 与 '人员编制'!A:C 进行关联。多数分析师采用的变通方案是用 {区域1, 区域2} 数组拼接将两个区域合并后再查询——但这要求两个区域的行数完全一致,且会产生脆弱的列引用。
NULL 处理。 GQL 将空白单元格视为 null,但在所有聚合场景中的传播行为并不稳定。SUM 和 AVG 均能正确忽略 null 值;但 WHERE Col3 is null 语法的表现,会因单元格是真正的空白还是包含零长度字符串而存在差异。实际操作中,建议加上 IFERROR 包裹,并在正式依赖 null 筛选之前用样本数据验证。
附一个性能参考:在现代 Sheets 实例中,将 5 万行交易记录压缩为整洁的 30 行成本中心汇总,QUERY 的运行时间不超过 2 秒。性能并非瓶颈——上述结构性限制才是。
编写不易出错的 QUERY 公式
在包含多个关联标签页的模型中,最常见的故障模式是 QUERY 公式使用位置引用(Col1、Col2)而非列字母。当上游有人插入一列时,所有基于位置的 QUERY 会静默偏移,且不会报任何错误。
在查询命名区域或架构稳定的标签页时,优先使用列字母:
=QUERY(INDIRECT("'利润表'!A:H"),
"SELECT A, C, D
WHERE C >= " & 假设参数!$B$3 & "
AND D <> ''
ORDER BY A",
1)
动态日期截止点——例如提取截至董事会汇报封账日的实际数据——通过将单元格引用值拼接进查询字符串来实现。上方示例中的 假设参数!$B$3 写法是正确的做法,不要将日期硬编码进查询字符串。
LABEL 子句常被忽视,但它能在不修改源数据的情况下重命名输出列,使董事会材料中的表格标题保持整洁,即便底层列名是内部编码缩写。建议善加使用:
=QUERY('收入明细'!A:E,
"SELECT B, SUM(E)
WHERE A = 'Q1'
GROUP BY B
LABEL B '业务单元', SUM(E) 'Q1 收入(元)'",
1)
针对多标签页模型:如果需要频繁跨工作表查询,建议先将源数据整合至单一数据暂存标签页。在 2 万行暂存区域上执行一次 QUERY,无论是运行速度还是可调试性,都优于在六个独立标签页上分别执行六次 QUERY。
QUERY 不再适用的场景
JOIN 限制是"何时应该放弃 QUERY"这个问题的真实答案。如果分析工作需要关联两个数据集——例如将 '应付账款' 中的 3400 张发票与 '资金部' 中按发票编号记录的付款记录进行匹配——QUERY 无法胜任。INDEX/MATCH 可处理一对一查找;SUMIFS 可处理聚合查找;但关系型关联查询要么需要辅助列,要么需要超出原生 QUERY 语法范畴的工具。
另一个实践中常遇到的限制:跨架构不同的工作表进行汇总。QUERY 无法动态合并 '一月'!A:F、'二月'!A:F、'三月'!A:F 的数据,除非显式使用 {'一月'!A:F; '二月'!A:F; '三月'!A:F} 进行拼接——标签页一多,这种写法便难以维护。
ModelMonkey 的 DuckDB 集成为上述场景提供了解决方案,支持对工作表区域运行真正的 SQL,包括跨标签页的 JOIN 操作。实际语法如下:
SELECT ap.invoice_id, ap.amount, t.payment_date
FROM sheet_range('应付账款!A:D') ap
JOIN sheet_range('资金部!A:C') t ON ap.invoice_id = t.invoice_id
WHERE ap.amount > 70000
当 QUERY 的限制开始持续消耗你的时间,这就是出口所在。具体配置方式请参阅 Google Sheets DuckDB 集成 一文。