QUERY(range, "SELECT a, b") 可以提取两列数据。如果你的使用止步于此,那么 QUERY 函数真正的价值你还没触及。
SELECT 子句支持聚合函数——SUM、COUNT、AVG、MAX、MIN——并可与 GROUP BY 和 PIVOT 组合使用。这种组合能将原始流水明细直接转换为汇总表,无需 SUMIFS、无需辅助列、也无需在每次新增行时手动扩展范围。大多数教程跳过了这部分,因为讲清楚需要花些功夫——而这恰恰是真正能改变你构建数据模型方式的核心。
列字母是范围相对的,不是工作表相对的
先说一个基础概念:QUERY 公式中的 a 并不代表工作表上的 A 列,而是你传入 QUERY 的范围的第一列。如果范围是 transactions!C:H,那么 a 对应 C 列,b 对应 D 列,以此类推。
这在数据不从 A 列开始时尤为重要——而这种情况几乎是常态。在源数据范围左侧插入一列,所有下游 QUERY 公式就会静默失效,没有任何报错提示。这是需要提前规划的问题。
还有一个容易踩坑的关联行为:你可以在 WHERE 条件中使用某列进行过滤,但不必在 SELECT 中输出它。SELECT a, b WHERE c = '市场部' 完全合法——c 列参与筛选,但不出现在结果中。很多人默认 WHERE 用到的列必须出现在 SELECT 里,其实不然。
用聚合 SELECT 替代 SUMIFS 矩阵
假设你的流水明细表有 5 列:日期、部门、费用类别、供应商、金额。你需要按部门和费用类别生成汇总——通常的做法是搭一个 SUMIFS 矩阵,对每个"部门×类别"组合硬编码一条公式,轻松写出 20 个。
=QUERY(transactions!A:E,
"SELECT B, C, SUM(E)
WHERE A >= date '2026-01-01'
GROUP BY B, C
ORDER BY B, SUM(E) DESC
LABEL SUM(E) '合计'")
这一个公式就能生成完整的汇总表。新增部门自动出现,新增费用类别自动出现,日期过滤器排除历史期间数据。末尾的 LABEL 子句将聚合列的默认标题("sum 金额")改为可读的中文名称。
在一张典型的 1 万行流水明细上,这种方式的运行时间约为 600 毫秒,而扫描相同范围的等效 SUMIFS 矩阵约需 1,100 毫秒。随着行数增加,差距会进一步拉大。
用于从明细表提取利润表数据时:加上 WHERE D = '主营业务成本' 可单独提取成本行,或从 GROUP BY 中去掉费用类别列,将数据折叠到仅按部门汇总。一个公式,多种视角。
PIVOT:无需手动重建的交叉表
QUERY 的 PIVOT 关键字可以生成随源数据变化自动更新的交叉表,无需手动添加列。
=QUERY(transactions!A:E,
"SELECT B, SUM(E)
WHERE A >= date '2026-01-01'
GROUP BY B
PIVOT C")
行是部门,列是 C 列中出现的所有不同值——动态生成,无需提前指定。QUERY 读取数据后,为每个唯一值创建一列。对于会随时间持续新增费用类别的报销数据集,这是唯一一种无需每季度手动添加列的方案。
一个真实的局限性:PIVOT 的列标题来自数据值,因此在下游公式中引用起来比较麻烦。如果输出结果需要参与进一步计算,仅使用 GROUP BY 是更稳妥的选择——交叉表的展示形式可以单独处理。
数据不干净时会发生什么
这是大多数 QUERY 教程不会告诉你的部分:SELECT 在干净数据上运行顺畅,真实数据会以特定的、难以诊断的方式让它出错。
类型推断是静默的。 QUERY 会扫描列的前一部分数据来判断类型。Google Sheets 官方文档将此称为"列类型推断",其行为与 BigQuery 的强类型 SQL 不同——BigQuery 在 CREATE TABLE 语句阶段即完成列类型绑定,而 Sheets QUERY 在每次求值时动态推断。如果你的金额列有 4,997 个数字,但有 3 个单元格被人工输入了"N/A"或"待确认",QUERY 可能会将整列判定为文本类型。此时 SUM(E) 返回空白——没有报错,没有提示。你只会在对账时发现汇总数据对不上。
解决方案:用辅助列写 =IFERROR(VALUE(E2), 0),将"N/A"转换为 0,数字保持不变,然后让 QUERY 引用辅助列而非原始列。
空行会造成同样的问题。 ERP 和数据库导出文件中经常包含间歇性的空行(在 SAP、Oracle 以及各类遗留系统的 CSV 导出中很常见)。这些空行会打断 QUERY 的类型检测扫描。在其他条件前加上 WHERE E IS NOT NULL 可以过滤掉空行。将范围定义为完整列(A:E)而非固定范围(如 A1:E5000)也有帮助——QUERY 在开放式范围上处理稀疏数据的效果更好。
混合日期格式是最棘手的问题。 QUERY 的 WHERE 子句对日期有格式要求:date 'YYYY-MM-DD'。如果日期列中有些值以文本字符串存储,有些以实际日期值存储,过滤器会静默地返回空结果。用 =TYPE(A2) 检测:结果为 1 表示数字(日期在 Sheets 中以数字存储,这是正确的),结果为 2 表示文本(异常)。文本日期需要在辅助列中用 =DATEVALUE(A2) 转换后,QUERY 才能正常过滤。
贯穿以上所有场景的失败模式是静默——返回空结果,而非错误信息。在将 QUERY 公式用于真实数据集之前,先运行 SELECT COUNT(a)。如果计数与预期行数不符,说明在进入正式查询之前,类型推断环节就已经出了问题。
动态 SELECT:下拉菜单驱动的报表
对于报表场景,最实用却鲜有文档记录的 QUERY 用法是:从单元格引用构建 SELECT 字符串。
=QUERY(data!A:F,
"SELECT " & B1 & " WHERE A >= date '2026-01-01'")
如果 B1 的内容是 "a, b, d",查询返回第 1、2、4 列。将 B1 改为 "a, c, f",输出立即变化。
将这个模式与数据验证下拉菜单结合——让用户选择"收入视图"或"成本视图"——再用 VLOOKUP 将每个选项映射到对应的列字符串,就可以构建一个根据用户输入动态重配置的单一输出区域。无需 Apps Script,无需自定义菜单。
实际应用场景:如果不同利益相关方需要不同的列子集(财务团队关注利润率,运营团队关注人员效率,管理层关注 KPI),这个模式可以从同一个数据标签页为所有人提供服务,无需维护三个独立的输出区域。插入列仍然会导致公式失效——这是字母列寻址的根本局限——但对于结构稳定的数据表,这是纯公式方案中最简洁的选择。
Google Sheets QUERY vs FILTER vs VLOOKUP 横向对比
| 任务 | 最佳工具 | 1 万行数据约耗时 |
|---|---|---|
| 按条件返回匹配行 | QUERY 或 FILTER | 约 400 毫秒(相近) |
| 按分组汇总(替代 SUMIFS 矩阵) | QUERY GROUP BY | 约 600 毫秒,同等 SUMIFS 约 1,100 毫秒 |
| 交叉表输出 | QUERY PIVOT | 约 900 毫秒 |
| 单值键值查找 | VLOOKUP / INDEX-MATCH | 约 80 毫秒 |
| 动态列选择 | QUERY 字符串拼接 | 与基础查询相同 |
| 正则 / 模糊行过滤 | QUERY(MATCHES 关键字) | 约 700 毫秒 |
VLOOKUP 在单值查找上优势明显——速度更快,公式也更易审计。当你不需要聚合时,FILTER 在条件行筛选上优于 QUERY,因为语法更直观,且原生支持数组条件。需要 GROUP BY、PIVOT 或聚合列选择时,QUERY 才是首选。用 QUERY 做简单的单值查找,就像开高速去楼下便利店——工具选错了。
真实场景:月度订阅收入汇总
上述模式在这里变得具体。假设你有一张名为 stripe_export 的标签页,包含以下列:日期、客户 ID、月度经常性收入(MRR)、套餐类型、状态。你需要在独立的报告标签页按套餐生成月度汇总。
=QUERY(stripe_export!A:E,
"SELECT
YEAR(A), MONTH(A), D,
SUM(C)
WHERE E = 'active'
GROUP BY YEAR(A), MONTH(A), D
ORDER BY YEAR(A), MONTH(A), D
LABEL YEAR(A) '年份', MONTH(A) '月份', D '套餐', SUM(C) 'MRR'")
这个公式输出一张 4 列表格:年份、月份、套餐、MRR。每次导出标签页更新,公式自动重算。无需额外公式,无需在新增套餐层级时扩展 SUMIFS 矩阵。
注意事项:如果 MRR 列中哪怕只有一个非数值——导出脚本将"null"或"—"写入而非写 0——SUM 就会对整列返回空白。在源数据上包一层:在辅助列写 =IFERROR(VALUE(C2), 0),然后让 QUERY 引用辅助列。
截至 2026 年 4 月,QUERY 的 YEAR() 和 MONTH() 函数可作用于日期格式的单元格,但无法处理看起来像日期的文本字符串。如果导出文件将日期写为文本("2026-01-15" 而非 Sheets 日期值),需添加辅助列 =DATEVALUE(A2) 并引用该列。
当列数较多时,字母列寻址很快就会变得混乱——一张 15 列的导出表尤其如此。ModelMonkey 可以直接为你生成 QUERY 字符串:描述你需要的汇总方式,它会自动生成公式、完成列映射,并在产生静默空白之前发现类型问题。查看 ModelMonkey 方案——支持 Google Sheets 和 Excel。
常见问题
Q:Google Sheets QUERY 的 SELECT 最多能选多少列?
没有官方的硬性上限,但根据实测,当 SELECT 子句超过 20 列且数据量超过 5 万行时,运算时间会显著增加。实际使用中,建议缩小数据范围并只选取必要列,而非全列扫描。
Q:QUERY 的 SELECT a, b 与直接引用单元格范围有什么区别?
直接范围引用(如 =A:B)只能原样输出数据,无法过滤、排序或聚合。SELECT a, b 是查询语言的一部分,可以与 WHERE、GROUP BY、ORDER BY 等子句组合,实现条件筛选和动态计算,是两种本质不同的操作方式。
Q:为什么我的 QUERY SELECT 公式返回空白而不是报错?
最常见原因是列类型推断失败:数字列中混入了文本值(如"N/A"、空字符串),QUERY 将整列判定为文本类型,导致 SUM 等聚合函数静默返回空白。先用 SELECT COUNT(a) 验证行数是否正常,再用 =TYPE() 逐列检查数据类型。
Q:QUERY 的 PIVOT 和手动创建的交叉表有什么实际差异?
PIVOT 的核心价值在于列自动扩展:数据中出现新的分类值时,列会自动增加,无需手动添加。手动交叉表在新增分类时需要同步修改公式结构。但 PIVOT 的列标题由数据值决定,在下游公式中难以稳定引用,适合展示用途而非计算用途。
Q:在 Google Sheets 中,QUERY 比 SUMIFS 慢还是快?
在聚合场景下,QUERY GROUP BY 通常快于等量的 SUMIFS 矩阵。以 1 万行数据为基准,QUERY GROUP BY 约需 600 毫秒,而覆盖相同维度组合的 SUMIFS 矩阵约需 1,100 毫秒。当 SUMIFS 公式数量增多(即维度组合增加)时,差距进一步扩大;QUERY 的计算成本与公式数量无关,仅与数据规模相关。