投影与筛选与聚合与分组

共 46 题
📑 题目列表 46 题
#
★★★

1. SELECT * 在生产环境的危害,列顺序变化、列裁剪(Column Trimming)失效、网络带宽浪费如何避免?

请说明 SELECT * 在生产环境中的危害(列顺序变化、列裁剪失效、网络带宽浪费),以及如何避免这些问题?

  • SELECT * 与列顺序不稳定问题
  • 投影裁剪(Column Trimming)与 IO 优化
  • 网络带宽与内存开销

SELECT * 的主要危害有三:其一,当表结构增减列或调整列顺序时,SELECT * 返回的列集合随之改变,依赖固定列顺序的应用程序(如按位置取值的程序、ETL 下游)极易出错,且结果集契约不稳定;其二,数据库无法对此查询做投影裁剪(Column Trimming),即无法在扫描阶段只读取需要的列,可能浪费大量 IO 与缓冲内存;其三,多取出的列会在网络传输中占用额外带宽,若表宽达几十上百列,结果集体积被显著放大。避免的方式是显式列出所需列名,让优化器能做列裁剪,并保证接口契约稳定。

显式列清单既稳定了结果集契约,又给优化器留出投影裁剪的空间,减少扫描与传输成本。这是生产 SQL 的通用最佳实践。

-- 不推荐:结果列随表结构变化
SELECT * FROM employee;
-- 推荐:显式列出所需列
SELECT emp_id, emp_name, salary FROM employee;
#
★★★

2. SELECT DISTINCT 的实现代价,去重是通过排序(Sort)还是哈希聚合(HashAggregate)?

请说明 SELECT DISTINCT 的实现代价,去重是依靠排序(Sort)还是哈希聚合(HashAggregate)?

  • DISTINCT 的两种底层实现
  • 排序与哈希的适用场景
  • 执行计划中的节点差异

SELECT DISTINCT 的底层实现通常有两种:对结果集排序后消除相邻重复(Sort + Unique 节点),或使用哈希聚合(HashAggregate)对输出列做去重。PostgreSQL 中当输出列较多、或排序键可复用已有有序数据时倾向 Sort+Unique,否则常用 HashAggregate;MySQL 对应使用 filesort 或临时表。哈希方式在数据量较大、内存充足时通常更快,以摊还 O(n) 开销完成去重;排序方式依赖排序键,若输入已有序则代价更低。无论哪种,DISTINCT 都带来额外开销,不能认为是廉价操作。

去重本质上是对组内多行取一行,这与聚合(GROUP BY)是同一类问题,因此数据库都复用聚合/排序基础设施来实现。选择哪一种由优化器根据数据量、内存与输入有序性决定。

#
★★★

3. SELECT 列表中的子查询(标量子查询)与函数调用在执行计划中的差异?

SELECT 列表中的标量子查询(Scalar Subquery)与函数调用在执行计划中的差异是什么?

  • 标量子查询与函数调用的本质区别
  • InitPlan 与 SubPlan 的执行时机
  • 稳定性与优化器改写

SELECT 列表中的标量子查询(如 SELECT (SELECT max(salary) FROM emp))在优化器里会被转化为 InitPlan(若与外表无关、可只执行一次)或 SubPlan(若相关,每行重新执行)。InitPlan 在查询开始前执行一次并缓存结果;SubPlan 随外层行逐行执行,代价更高。而函数调用(如 UPPER(col)、random())在投影阶段直接对每行求值,通常没有子计划。函数又分稳定(STABLE,同参数同结果)与非稳定(VOLATILE,如 random()、now()),volatile 函数每行都重新计算,且不能用于创建索引等场景。

关键差异在于:标量子查询可能被优化为只执行一次的计划(InitPlan),而 volatile 函数会对每行求值。理解这一点可避免写出行级重复执行的昂贵子查询。

-- 非相关标量子查询:通常只执行一次(InitPlan)
SELECT name, (SELECT max(salary) FROM emp) AS top_salary FROM dept;
-- 相关标量子查询:每行执行(SubPlan)
SELECT name, (SELECT max(salary) FROM emp e WHERE e.dept_id = d.id) FROM dept d;
#
★★★

4. SELECT 子句的执行阶段在 WHERE 之后、ORDER BY 之前,请用关系代数符号写出 select * from t where x>1 order by y 的执行树。

SELECT 子句(投影)执行阶段在 WHERE 之后、ORDER BY 之前,请用关系代数写出 select * from t where x>1 order by y 的执行树?

  • SQL 逻辑执行顺序
  • 关系代数操作符(σ、π、τ)
  • 投影与排序的先后

该查询的逻辑执行顺序是:先扫描关系 t,用选择操作 σ 过滤 x>1,再对结果做投影 π(此处为 *,即全部列),最后按 y 排序(τ)。关系代数执行树如下:

τ_y (排序 ORDER BY y)
   └── π_* (投影 SELECT *)
        └── σ_{x>1} (选择 WHERE x>1)
             └── t (表)

即 τ_y(π_*(σ_{x>1}(t)))。注意这里是按逻辑/语义顺序;实际物理执行计划可能调整(例如在索引扫描时同时完成过滤与排序),但逻辑顺序不变。

SELECT 投影在 WHERE 之后、ORDER BY 之前,这是 SQL 标准语义。本题考察把逻辑处理顺序翻译成关系代数树的能力。

#
★★★

5. WHERE 子句中的常见谓词(=, <>, >, <, BETWEEN, IN, LIKE, IS NULL, EXISTS)如何映射到 B-Tree 索引?哪些谓词会导致索引失效?

WHERE 子句中的常见谓词(=、<>、>、<、BETWEEN、IN、LIKE、IS NULL、EXISTS)如何映射到 B-Tree 索引?哪些谓词会导致索引失效?

  • 等值/范围谓词与 B-Tree 索引
  • SARGable 与非 SARGable 谓词
  • 索引失效的常见原因

对于 B-Tree 索引,=、>、<、>=、<=、BETWEEN(等价于范围条件)、IN(等价于多个等值条件)都是可走索引的"SARGable"谓词,可通过索引定位到范围。LIKE 只要通配符不在开头(如 'abc%')即可走索引做前缀匹配;'%abc' 或 '%abc%' 会导致无法用索引前缀。IS NULL / IS NOT NULL 在普通 B-Tree 索引上一般可走索引(PostgreSQL 默认索引含 NULL,可做空值扫描)。<>(不等)一般不能有效利用索引范围扫描,只能做全表或索引扫描后过滤。EXISTS 本身是半连接,可通过索引连接实现。导致索引失效的常见原因:对列施加函数(WHERE fun(col)=v 无表达式索引)、隐式类型转换、LIKE 前导通配符、OR 分支无法合并、列参与算术运算。

判断谓词能否走 B-Tree 的关键是"能否仅用键值大小直接定位"。等值与范围条件满足;非等值、对列做函数/运算、前导通配符不满足。NULL 扫描在多数数据库可走索引。

#
★★★

6. TABLESAMPLE 的 SYSTEM(按数据块随机)与 BERNOULLI(按行独立随机)两种采样方法在样本均匀性、执行成本与统计信息收集上的差异是什么?

TABLESAMPLE 的 SYSTEM(按数据块随机)与 BERNOULLI(按行独立随机)两种采样方法在样本均匀性、执行成本与统计信息收集上的差异是什么?

  • SYSTEM 与 BERNOULLI 的采样原理
  • 均匀性与 I/O 成本
  • 统计信息收集的偏差

SYSTEM 方法按数据块(页面)为单位随机选取,被选中的块内所有行全部返回,因此样本在块粒度上均匀,但行粒度上可能聚集(同一块内行被一起选中),均匀性较差,尤其当数据按块呈聚簇分布时偏差明显;优点是只读少数块,I/O 成本低、速度快。BERNOULLI 方法对每一行独立地以给定概率决定是否保留,样本在行粒度上更均匀、更接近真实分布,但需要扫描全部数据块,I/O 成本高。用于统计信息收集(如 ANALYZE)时,BERNOULLI 更准确,但成本高;SYSTEM 快但可能产生偏差。

两者是"以块抽样"与"以行抽样"的代表。实用中,若数据分布均匀且要求速度选 SYSTEM,若要求代表性选 BERNOULLI。ANALYZE 默认用按块采样,大数据量下可接受。

SELECT * FROM orders TABLESAMPLE SYSTEM (10);   -- 约 10% 的块
SELECT * FROM orders TABLESAMPLE BERNOULLI (10); -- 约 10% 的行
#
★★★

7. WHERE 子句中对 NULL 的处理(IS NULL、IS NOT NULL)能否走索引?

WHERE 子句中对 NULL 的处理(IS NULL、IS NOT NULL)能否走索引?

  • B-Tree 索引对 NULL 的存储
  • IS NULL 的索引扫描
  • 数据库差异

在 PostgreSQL 中,普通 B-Tree 索引默认包含 NULL 值(NULL 排在最后,NULLS LAST),因此 WHERE col IS NULL 可以走索引进行空值扫描,IS NOT NULL 也可走索引(但通常需要扫描大量索引条目,代价接近全表扫描)。Oracle 的 B-Tree 索引不存储全 NULL 的行,因此 IS NULL 无法直接走索引,只能通过函数索引或位图索引等实现。MySQL 的 InnoDB B-Tree 索引可以包含 NULL,IS NULL 通常可走索引。整体而言,IS NULL 是否能走索引取决于数据库对 NULL 的索引存储策略。

关键不在 SQL 语法,而在索引是否包含 NULL 条目。PostgreSQL 默认含 NULL,故可走;Oracle 不含,故无法直接走。这是数据库差异的典型考点。

#
★★★

8. PostgreSQL 中 SELECT INTO 与 CREATE TABLE AS SELECT 在语义与事务行为上有何差异?二者为何都不继承源表的索引、约束与默认值?

PostgreSQL 中 SELECT INTO 与 CREATE TABLE AS SELECT 在语义与事务行为上有何差异?为什么二者都不继承源表的索引、约束与默认值?

  • SELECT INTO 与 CREATE TABLE AS (CTAS) 的语义
  • 事务行为(OID、日志)
  • 新表与源表的关系

在 PostgreSQL 中,SELECT INTO 与 CREATE TABLE AS SELECT(CTAS)语义基本等价,都是"根据查询结果创建新表",且都不继承源表的索引、约束、默认值等。差异体现在:CTAS 是标准 SQL 且更直观,支持指定表空间、存储参数等;SELECT INTO 在早期版本中对于临时表不写入 WAL 日志,某些版本行为略有不同,但现代版本两者行为已趋同。它们都不继承源表结构(索引、约束、默认值)的原因:新表是基于"查询结果"而非"源表定义"创建的,数据库只复制查询输出的列与其类型,不复制源表的元数据对象。如果需要继承,需用 CREATE TABLE(LIKE ... INCLUDING ALL)再 INSERT。

这两者本质都是"结果集物化",因此只保留列类型而不保留约束。若需复制约束,应显式定义。CTAS 是更推荐、更标准的写法。

#
★★★

9. DISTINCT ON(PostgreSQL)的用法?

PostgreSQL 中 DISTINCT ON 的用法是什么?

  • DISTINCT ON 的语法
  • 与 ORDER BY 的配合
  • 与 GROUP BY / ROW_NUMBER 的取舍

DISTINCT ON (expr) 是 PostgreSQL 特有语法,它会按 expr 分组,并对每组返回第一行(组内顺序由 ORDER BY 决定)。因此 DISTINCT ON 后必须配合 ORDER BY,且 ORDER BY 的起始列必须包含 DISTINCT ON 的表达式,否则结果不确定。它可以实现"每组取一条"(如每个部门工资最高的人)。注意:DISTINCT ON 返回的是整行,而非聚合后的列,这与 GROUP BY 不同。

DISTINCT ON 是"每组取第一行"的便捷写法,等价于用 ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...)=1 再过滤,但语义更直接。注意与 GROUP BY 的"只保留分组键"区分。

-- 每个部门工资最高的人
SELECT DISTINCT ON (dept_id) dept_id, emp_name, salary
FROM employee
ORDER BY dept_id, salary DESC;
#
★★★

10. DISTINCT 与 GROUP BY 的等价关系?

DISTINCT 与 GROUP BY 在什么情况下等价?有何差异?

  • DISTINCT 与 GROUP BY 的等价条件
  • 投影与聚合的差异
  • 执行计划差异

当 SELECT 列表中的列与 GROUP BY 的列一致且不使用聚合函数时,两者等价。例如 SELECT DISTINCT a, b FROM t 与 SELECT a, b FROM t GROUP BY a, b 结果相同。差异在于:GROUP BY 允许同时使用聚合函数(如 SELECT a, COUNT(*) FROM t GROUP BY a),而 DISTINCT 不能;GROUP BY 的语义是"分组",DISTINCT 的语义是"去重";执行计划上,GROUP BY 用 GroupAggregate/HashAggregate,DISTINCT 用 Unique 节点或 HashAggregate,在优化器内部两者常被统一处理。当存在聚合时,DISTINCT 无法替代 GROUP BY。

核心结论:无聚合时 DISTINCT 与 GROUP BY 等价,有聚合时只能用 GROUP BY。优化器常把无聚合的 GROUP BY 改写为去重。

#
★★★

11. ILIKE 与 LIKE 的差异(PostgreSQL)?

PostgreSQL 中 ILIKE 与 LIKE 的差异是什么?

  • 大小写敏感性
  • 索引支持
  • 与 lower() 的取舍

LIKE 是大小写敏感匹配,ILIKE 是大小写不敏感匹配(等价于 LIKE 的忽略大小写版本)。两者都支持 % 和 _ 通配符。在索引上,普通 B-Tree 索引对 ILIKE 无效(因为大小写不敏感无法用默认 collation 的前缀匹配),除非建表达式索引(如 lower(col))或使用 pg_trgm 索引。若想大小写不敏感且可走索引,可建 lower(col) 表达式索引,再写 WHERE lower(col) LIKE 'abc%'。

ILIKE 是 PostgreSQL 对 LIKE 的大小写不敏感扩展。性能上 ILIKE 因无法利用普通索引前缀扫描,可能需要全表扫描,除非使用表达式索引或 pg_trgm。

SELECT * FROM t WHERE name ILIKE 'abc%';  -- 大小写不敏感
SELECT * FROM t WHERE name LIKE 'abc%';   -- 大小写敏感
#
★★★

12. PostgreSQL 中 SELECT 的输出行数估算(pg_stat)如何查询?

PostgreSQL 中如何查询 SELECT 的输出行数估算(通过统计信息)?

  • pg_class.reltuples
  • pg_stat_user_tables 视图
  • ANALYZE 与估算精度

PostgreSQL 中表的估算行数可以从 pg_class 的 reltuples 字段读取(该字段由 ANALYZE 更新),也可从 pg_stat_user_tables 视图的 n_live_tup 读取。但要区分"表中的估算行数"与"SELECT 输出行数":前者用 reltuples,后者是优化器对查询做的基数估算,可用 EXPLAIN 查看 rows 估算。n_live_tup 是 VACUUM 后更新的活行估计,更接近实际。注意这些统计值不是精确的,依赖上次 ANALYZE/VACUUM 的时间。

优化器用这些统计信息做基数估算。若统计信息过期,估算会偏差,导致执行计划不佳。定期 ANALYZE 可保持估算准确。

SELECT relname, reltuples FROM pg_class WHERE relname = 'orders';
SELECT n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE relname = 'orders';
#
★★★

13. PostgreSQL 中如何用 EXPLAIN 查看 SELECT 的执行路径?

PostgreSQL 中如何用 EXPLAIN 查看 SELECT 的执行路径?

  • EXPLAIN 基础语法
  • ANALYZE、BUFFERS、COSTS 选项
  • 读懂执行计划

使用 EXPLAIN 前置即可查看执行计划,如 EXPLAIN SELECT ...。常用选项:EXPLAIN ANALYZE 会真正执行并输出每个节点的实际耗时与行数;EXPLAIN (ANALYZE, BUFFERS) 额外显示缓冲命中情况;EXPLAIN VERBOSE 显示更多细节;EXPLAIN (COSTS, FORMAT JSON) 输出 JSON 格式。执行计划自底向上读取,每个节点显示 scan 方式(Seq Scan、Index Scan)、cost、rows、actual time。ANALYZE 会真正执行语句,因此不能用于 DML 的误操作测试(可用 BEGIN ... EXECUTE ... ROLLBACK 包裹)。

EXPLAIN 是调优的核心工具。EXPLAIN 只给估算,EXPLAIN ANALYZE 给实际执行数据,二者对比可发现估算偏差。

EXPLAIN SELECT * FROM orders WHERE user_id = 100;
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE user_id = 100;
#
★★★

14. SELECT * 与 SELECT col1, col2, col3 在网络传输上的差异?

SELECT * 与 SELECT col1, col2, col3 在网络传输上的差异是什么?

  • 传输数据量
  • 列裁剪与投影
  • 宽表场景

在传输层,SELECT * 会返回表的所有列,SELECT col1, col2, col3 只返回显式列出的列。若表只有 3 列,两者传输量相同;若表有数十上百列,SELECT * 会多传输大量列的数据,成倍放大网络带宽与序列化开销。此外,SELECT * 不利于列裁剪(数据库无法在扫描阶段只读需要的列),加重 IO 与缓冲。因此 SELECT * 在网络传输与 IO 上通常比显式列更高,尤其在宽表、大结果集、远程查询场景差异显著。

差异的本质是"返回数据量"。显式列清单既减少传输量,又允许投影裁剪。结果集列数越多,差距越大。

#
★★★

15. SELECT FROM WHERE 与 SELECT FROM WHERE ORDER BY 的执行顺序差异?

SELECT FROM WHERE 与 SELECT FROM WHERE ORDER BY 的执行顺序差异是什么?

  • 逻辑执行顺序
  • ORDER BY 的介入
  • 排序的代价

两者的逻辑执行顺序大致相同:FROM → WHERE → SELECT(投影)→(可选 ORDER BY)。差异在于:带 ORDER BY 的查询在投影之后增加一个排序阶段 τ,对结果集按指定列排序。排序可能产生额外代价(Sort 节点,可能落盘到临时文件),甚至可能影响执行计划(例如优化器可能改用索引扫描利用索引顺序避免显式排序)。不带 ORDER BY 时结果行序不确定,数据库可按任意顺序返回;带 ORDER BY 则保证投递顺序。

ORDER BY 不改变过滤与投影,只增加排序步骤。是否排序由优化器决定(可借助索引消去排序)。SQL 逻辑顺序是 FROM→WHERE→SELECT→ORDER BY。

#
★★★

16. SELECT INTO 的两种语义(PostgreSQL 创建新表 vs SELECT 结果赋给变量)?

SELECT INTO 的两种语义(PostgreSQL 创建新表 vs 把查询结果赋给变量)是什么?

  • 顶层 SELECT INTO 建表
  • PL/pgSQL 中 SELECT INTO 变量
  • 语义区分

PostgreSQL 中 SELECT INTO 有两种语义:在顶层 SQL 中,SELECT INTO new_table FROM ... 用于创建新表并把查询结果写入(等价于 CTAS);在 PL/pgSQL 函数中,SELECT expr INTO var FROM ... 用于把查询结果赋给标量变量或记录变量。区分依据是上下文:顶层是建表,过程语言内是赋值。在 PL/pgSQL 中,若查询返回多行只取第一行,注意用 LIMIT 或确保单行,以免取到非预期行。

同一关键字在 SQL 与 PL/pgSQL 中含义不同。顶层建表,过程内赋值是两类场景,需结合上下文理解。

-- 顶层:建表
SELECT id, name INTO new_emp FROM employee;
-- PL/pgSQL:赋值
DECLARE v_name text;
BEGIN
  SELECT name INTO v_name FROM employee WHERE id = 1;
END;
#
★★★

17. SELECT 列表的列能否重复?SELECT col, col FROM t 含义?

SELECT 列表的列能否重复?SELECT col, col FROM t 的含义是什么?

  • 重复列是否合法
  • 结果集列名
  • 与 DISTINCT 的交互

在多数数据库(如 PostgreSQL、MySQL)中,SELECT 列表允许出现重复列,如 SELECT col, col FROM t 会返回两列相同的数据,列名可能被自动区分(如 col、col)。但注意:如果配合 DISTINCT(SELECT DISTINCT col, col FROM t),去重是以"行"为单位,重复列不会影响去重结果(因为两列值相同)。虽然语法允许,但生产上应避免重复列,因为无意义且容易造成下游列名歧义。

重复列是合法但无意义的写法。数据库会原样返回多列相同值。DISTINCT 按整行去重,重复列不影响其结果。

#
★★★

18. SELECT 子句中能否使用表达式?SELECT col+1, col*2 FROM t 的执行计划如何?

SELECT 子句中能否使用表达式?SELECT col+1, col*2 FROM t 的执行计划如何?

  • 表达式投影
  • 每行求值
  • 表达式是否可下推

SELECT 子句可以使用任意表达式,如 SELECT col+1, col*2 FROM t。这些表达式在投影阶段对每一行求值,执行计划中通常表现为一个投影(Projection)或作为 Seq Scan/Index Scan 的输出列计算,没有额外的节点(除非产生新的排序等)。每个表达式在扫描节点之上逐行计算。若表达式是 immutable 的,优化器可能对常量做预计算;若表达式对应列上的函数,则每行调用。表达式本身不改变表的访问方式(除非建表达式索引)。

表达式投影是"每行就地计算",不引入额外扫描节点。理解它可知表达式一般只带来 CPU 开销,不改变 IO 访问路径。

SELECT col+1, col*2 FROM t;
#
★★★

19. WHERE col LIKE 'abc%' 能否走索引?

WHERE col LIKE 'abc%' 能否走索引?

  • 前缀通配符与索引
  • B-Tree 前缀匹配
  • collation 与索引限制

可以。当通配符 % 不出现在开头时,LIKE 'abc%' 是前缀匹配,可映射为 B-Tree 索引的范围扫描(等价于 col >= 'abc' AND col < 'abd'),因此能走索引。前提是列上的 collation 与索引使用的 collation 一致(默认 C 或合适的 collation)。若 collation 不一致(如非确定性 collation),或使用了非默认 collation,则可能无法走索引,此时可用 text_pattern_ops 操作符类索引。前导通配符('%abc')不能走索引。

LIKE 'abc%' 本质是前缀范围扫描,B-Tree 可支持。这要求 collation 与索引一致。

#
★★★

20. WHERE 子句中表达式索引的使用条件?

WHERE 子句中表达式索引的使用条件是什么?

  • 表达式索引的定义
  • 与查询表达式完全一致
  • 稳定函数限制

表达式索引(Expression Index)用于对列上的函数或表达式建立索引,如 upper(col)、substring(col,1,3) 等。使用条件是:查询中的 WHERE 表达式必须与索引表达式在结构上完全一致(包括函数名、参数、类型转换),且所用函数必须是 IMMUTABLE(不可变的),因为索引内容依赖函数对每个值给出确定结果。建立表达式索引后,WHERE 中写相同表达式时优化器才能匹配该索引。

表达式索引的关键是"查询表达式与索引表达式逐字一致",且函数必须 immutable。若查询写法不同(如少了 lower())则无法匹配。

CREATE INDEX idx_lower ON employee (lower(name));
SELECT * FROM employee WHERE lower(name) = 'dave';  -- 可走 idx_lower
#
★★★

21. GROUP BY 的语义,分组后每组输出单行。SELECT 列表为何只能包含分组键或聚合函数?

GROUP BY 的语义是分组后每组输出单行。为什么 SELECT 列表只能包含分组键或聚合函数?

  • GROUP BY 的分组语义
  • 每组单行的输出约束
  • ONLY_FULL_GROUP_BY 与标准

GROUP BY 把表按指定列划分为若干组,每组在结果中只输出一行。由于每组内其他列可能有多个不同的值,SELECT 列表若引用非分组键、非聚合的列,则无法确定该显示哪个值,因此在标准 SQL 下是非法/不确定的。SELECT 列表只能包含:分组键列、聚合函数(对整个组计算)、或分组键的表达式。MySQL 在非 ONLY_FULL_GROUP_BY 模式下会返回组内某一行(不确定,通常为扫描到的第一行),这会导致结果不确定;开启 ONLY_FULL_GROUP_BY 后则严格报错。

分组后每组一行,非分组的普通列"值不唯一",无法确定输出,故被禁止。聚合函数把多行压缩为一行,是合法的。这是 GROUP BY 的核心约束。

#
★★★

22. HAVING 与 WHERE 的执行顺序与语义差异,HAVING 在分组后过滤。

HAVING 与 WHERE 的执行顺序与语义差异是什么?HAVING 在分组后过滤?

  • WHERE 先于分组过滤行
  • HAVING 在分组后过滤组
  • 聚合条件只能用 HAVING

WHERE 在分组之前对原始行进行过滤,作用于单行;HAVING 在分组与聚合之后对分组结果进行过滤,作用于组,因此 HAVING 中可以使用聚合函数(如 HAVING COUNT(*) > 5),而 WHERE 中不能使用聚合函数。逻辑顺序:FROM → WHERE → GROUP BY → 聚合 → HAVING → SELECT → ORDER BY。能放在 WHERE 的条件应放在 WHERE,因为先过滤行再分组可减少分组的数据量,性能更好。

WHERE 过滤行、HAVING 过滤组。涉及聚合的判断必须用 HAVING,单纯行过滤应尽量用 WHERE 以提升性能。

#
★★

23. PostgreSQL 中 FILTER (WHERE ...) 子句与 CASE WHEN 聚合的等价写法与性能差异?

PostgreSQL 中 FILTER (WHERE ...) 子句与 CASE WHEN 聚合的等价写法与性能差异是什么?

  • FILTER 子句语法
  • CASE WHEN 聚合等价写法
  • 性能差异

PostgreSQL 支持 FILTER (WHERE ...) 对聚合做条件过滤,例如 COUNT(*) FILTER (WHERE status='a')。等价写法是 COUNT(CASE WHEN status='a' THEN 1 END)(CASE 不匹配时返回 NULL,被 COUNT 忽略)。两者的结果等价。性能上 FILTER 通常更高效,因为优化器可以更精确地做条件判断,且避免 CASE 表达式的某些开销;CASE WHEN 写法在部分数据库(如 MySQL)是唯一选择。FILTER 是 PostgreSQL 9.4+ 的语法,也被 SQL:2003 标准支持。

FILTER 与条件聚合 CASE WHEN 结果等价,但 FILTER 更简洁、表达更清晰,性能通常略优。这是条件聚合的两种写法。

SELECT dept_id,
       COUNT(*) FILTER (WHERE status='active') AS active_cnt,
       COUNT(CASE WHEN status='active' THEN 1 END) AS active_cnt2
FROM employee GROUP BY dept_id;
#
★★

24. 假设集聚合(Hypothetical-Set Aggregate),RANK() WITHIN GROUP 的用法?

假设集聚合(Hypothetical-Set Aggregate)中 RANK() WITHIN GROUP 的用法是什么?

  • 假设集聚合概念
  • WITHIN GROUP 语法
  • 与窗口函数 RANK 的区别

假设集聚合(Hypothetical-Set Aggregate)是 SQL:2016 标准特性,它"假设一个值插入到有序集合中,计算其排名(rank)"。PostgreSQL 提供了 rank()、dense_rank()、percent_rank()、cume_dist() 的 WITHIN GROUP 形式。用法:SELECT rank(3) WITHIN GROUP (ORDER BY score) FROM t; 表示"如果值 3 插入到 score 的有序序列中,它的排名是多少"。它返回单行聚合结果,与窗口函数 RANK() OVER 不同——窗口函数为每行计算实际排名,假设集聚合为给定值计算假设排名。

假设集聚合把"一个假设值"作为参数,返回值在其有序集合中的位置。常用于分位数、排名分析,是理论性较强的 SQL 特性。

SELECT rank(3) WITHIN GROUP (ORDER BY score) AS r FROM scores;
#
★★

25. 有序集聚合(Ordered-Set Aggregate),PERCENTILE_CONT、PERCENTILE_DISC 与 WITHIN GROUP 子句的用法?

有序集聚合(Ordered-Set Aggregate)中 PERCENTILE_CONT、PERCENTILE_DISC 与 WITHIN GROUP 子句的用法是什么?

  • 有序集聚合概念
  • PERCENTILE_CONT 与 DISC 的差异
  • WITHIN GROUP 语法

有序集聚合(Ordered-Set Aggregate)对有序集合返回单个聚合值,典型代表是 PERCENTILE_CONT 和 PERCENTILE_DISC。用法:PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY score) 返回中位数(连续型,插值);PERCENTILE_DISC(0.5) 返回离散型的第 50 百分位(实际存在的值)。PERCENTILE_CONT 在区间内做线性插值,结果可能不是原始数据中的值;PERCENTILE_DISC 返回实际数据点。两者都要求 WITHIN GROUP (ORDER BY ...) 指定排序。

CONT 是连续插值,DISC 是离散取整。中位数计算常用 PERCENTILE_CONT(0.5)。这是求分位数/中位数的标准方法。

SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY score) AS median,
       percentile_disc(0.5) WITHIN GROUP (ORDER BY score) AS median_disc
FROM scores;
#
★★

26. 聚合函数中的 DISTINCT 与 ALL 修饰符,COUNT(DISTINCT col) 与 COUNT(col) 的实现差异。

聚合函数中的 DISTINCT 与 ALL 修饰符:COUNT(DISTINCT col) 与 COUNT(col) 的实现差异是什么?

  • DISTINCT/ALL 修饰符
  • COUNT(DISTINCT) 的去重代价
  • 实现方式

COUNT(col) 统计非 NULL 值的个数,COUNT(DISTINCT col) 统计去重后的非 NULL 值个数。实现上,COUNT(DISTINCT col) 需要先对 col 去重(用排序或哈希),代价明显高于 COUNT(col),因为去重需要额外内存/临时空间。COUNT(col) 可以直接累加计数(忽略 NULL)。DISTINCT 修饰符可应用于多数聚合(SUM、AVG、COUNT 等),但 COUNT(DISTINCT) 是最常见的。ALL 是默认修饰符,可省略。

DISTINCT 修饰符意味着去重,去重需要排序或哈希,代价高。COUNT(DISTINCT) 特别昂贵,在大数据量下应谨慎。

#
★★

27. 聚合函数的统计相关性(CORR、COVAR_POP、REGR_SLOPE)如何在查询中使用?

聚合函数的统计相关性(CORR、COVAR_POP、REGR_SLOPE)如何在查询中使用?

  • 统计聚合函数
  • 相关系数计算
  • 使用场景

PostgreSQL 提供统计聚合函数:CORR(y, x) 计算相关系数(-1 到 1),COVAR_POP(y, x) 计算总体协方差,COVAR_SAMP 计算样本协方差,REGR_SLOPE(y, x) 计算线性回归斜率,REGR_INTERCEPT 计算截距。这些函数在 GROUP BY 分组内对列对计算统计量。用法:SELECT corr(score, hours) FROM study GROUP BY dept; 用于分析两个变量的相关性、回归趋势等。

统计聚合函数在单条 SQL 内实现统计计算,无需外部统计工具。常用于相关性分析、回归分析。

SELECT dept, corr(score, study_hours) AS corr,
       regr_slope(score, study_hours) AS slope
FROM study GROUP BY dept;
#
★★

28. 聚合函数(COUNT、SUM、AVG、MIN、MAX)在 NULL 上的处理规则,COUNT(*) 与 COUNT(col) 的差异。

聚合函数(COUNT、SUM、AVG、MIN、MAX)在 NULL 上的处理规则是什么?COUNT(*) 与 COUNT(col) 的差异?

  • 聚合对 NULL 的忽略
  • COUNT(*) 与 COUNT(col) 差异
  • 空集时的返回值

聚合函数对 NULL 的处理规则:COUNT() 统计所有行(含 NULL),COUNT(col) 只统计非 NULL 的行;SUM、AVG、MIN、MAX 都忽略 NULL(NULL 不参与计算)。若所有行都是 NULL(或空集),SUM、AVG、MIN、MAX 返回 NULL,COUNT 返回 0。AVG 是 SUM(col)/COUNT(col)(只统计非 NULL),所以 AVG 不会因 NULL 出错。COUNT() 与 COUNT(col) 的核心差异是 COUNT(*) 统计行数,COUNT(col) 统计非 NULL 行数。

记住"聚合忽略 NULL"(除 COUNT(*) 外)。空集时 SUM/AVG/MIN/MAX 返回 NULL,COUNT 返回 0。这是 NULL 处理的经典考点。

#
★★

29. 聚合函数(sum/avg)的精度与溢出,整数聚合溢出如何处理?DECIMAL 与 NUMERIC 类型聚合?

聚合函数(sum/avg)的精度与溢出如何处理?整数聚合溢出、DECIMAL 与 NUMERIC 类型聚合?

  • 整数聚合溢出
  • 精度提升
  • NUMERIC 聚合

整数类型的 SUM 可能溢出(如 int 的超出范围)。PostgreSQL 中 sum(int) 会提升为 bigint 以避免溢出,但 sum(bigint) 仍可能溢出。DECIMAL/NUMERIC 类型聚合的精度取决于输入精度,且当总和超过定义精度时仍可能溢出。避免溢出的方法:将列 CAST 为更高精度的类型(如 bigint、numeric)再聚合,或用 NUMERIC 类型。AVG 对整数返回 numeric,避免精度丢失。MySQL 中 SUM(int) 返回 DECIMAL,AVG(int) 返回 DECIMAL,自动提升精度。

溢出是整数聚合的隐患。数据库通常做类型提升(int→bigint),但大数仍可能溢出,需显式 CAST 到 numeric。DECIMAL 聚合精度受列定义精度限制。

SELECT SUM(amount::numeric) AS total FROM orders;  -- 避免溢出
SELECT AVG(price) FROM orders;  -- 整数 AVG 返回 numeric
#
★★

30. SELECT col, COUNT(*) FROM t 的合法性,col 不在 GROUP BY 时报错?

SELECT col, COUNT(*) FROM t 的合法性:col 不在 GROUP BY 时报错?

  • 非分组列 + 聚合的合法性
  • ONLY_FULL_GROUP_BY
  • 数据库差异

在标准 SQL 及严格模式(PostgreSQL、MySQL 的 ONLY_FULL_GROUP_BY、SQL Server 默认)下,SELECT col, COUNT() FROM t(无 GROUP BY)会报错,因为 col 既不在 GROUP BY 中,也不是聚合函数,无法确定每组该显示哪个 col 值。MySQL 在关闭 ONLY_FULL_GROUP_BY 时不会报错,会返回某一行(不确定)的 col 值,这是非标准行为。因此正确写法是 SELECT col, COUNT() FROM t GROUP BY col。

无 GROUP BY 时整个表是一组,输出一行。若 SELECT 里同时有普通列和聚合,普通列非法。需加 GROUP BY 分组。

#
★★

31. GROUP BY 与 DISTINCT 的执行计划差异?

GROUP BY 与 DISTINCT 的执行计划差异是什么?

  • 执行计划节点
  • 去重与聚合
  • 优化器改写

无聚合的 GROUP BY 与 DISTINCT 在语义上等价,执行计划也常被统一处理:GROUP BY 用 GroupAggregate 或 HashAggregate 节点,DISTINCT 用 Unique 或 HashAggregate 节点。差异在于:有聚合的 GROUP BY 必须做聚合计算(累加、分组),计划更复杂;DISTINCT 只去重。优化器常把无聚合的 GROUP BY 改写为 DISTINCT 或反之。底层都需要排序或哈希来分组/去重。整体上两者计划结构相似,区别在于是否带聚合函数。

语义等价时执行计划趋同。GROUP BY 有聚合时需多算聚合值,DISTINCT 无聚合。排序或哈希是两者的公共基础。

#
★★

32. GROUPING() 函数与 ROLLUP 的配合用法?

GROUPING() 函数与 ROLLUP 的配合用法是什么?

  • ROLLUP 分组层次
  • GROUPING() 函数
  • 小计行识别

ROLLUP 生成含小计/总计的分组层次,例如 GROUP BY ROLLUP(a, b) 会生成 (a,b)、(a)、(空) 三种分组。GROUPING(col) 函数返回 0 或 1,用于判断某列在当前分组中是否被聚合(即该行是否为小计行)。当 col 被 ROLLUP 聚合(不参与分组)时 GROUPING(col) 返回 1,否则返回 0。可用它区分小计/总计行,并在输出中显示如 '总计'、'小计' 等标签。

GROUPING() 是识别 ROLLUP/CUBE/GROUPING SETS 中小计行的工具。配合 ROLLUP 可标签小计、总计行。

SELECT dept, job,
       GROUPING(dept) AS d, GROUPING(job) AS j,
       COUNT(*)
FROM employee GROUP BY ROLLUP(dept, job);
#
★★

33. PostgreSQL 中 percentile_cont(0.5) WITHIN GROUP (ORDER BY col) 的含义?

PostgreSQL 中 percentile_cont(0.5) WITHIN GROUP (ORDER BY col) 的含义是什么?

  • 第 50 百分位
  • 连续插值
  • 中位数

percentile_cont(0.5) WITHIN GROUP (ORDER BY col) 计算 col 的第 50 百分位(即中位数)。cont 表示连续型,当数据点不足以直接取到 0.5 位置时,在相邻两个值之间做线性插值,因此返回值可能不是原始数据中的实际值。例如对 [1,2,3,4],0.5 位置在 2 和 3 之间,插值得到 2.5。若想返回实际存在的值,用 percentile_disc(0.5)。

percentile_cont(0.5) 是求中位数的标准方法,特点是连续插值(可能非原始值)。区分 cont 与 disc 是关键。

#
★★

34. PostgreSQL 中 stddev_pop 与 stddev_samp 的差异?

PostgreSQL 中 stddev_pop 与 stddev_samp 的差异是什么?

  • 总体标准差 vs 样本标准差
  • 分母 n 与 n-1
  • 使用场景

stddev_pop 计算总体标准差(分母为 n),stddev_samp 计算样本标准差(分母为 n-1)。当数据是全部总体时用 stddev_pop,是抽样样本时用 stddev_samp(贝塞尔校正)。样本标准差更常用于统计推断,因为 n-1 提供无偏估计。数值上,stddev_samp 通常大于 stddev_pop。对应方差函数 var_pop、var_samp。若数据只有一个值,stddev_samp 返回 NULL(除零),stddev_pop 返回 0。

区别仅在于分母 n 与 n-1。总体用 pop,样本用 samp。样本标准差是无偏估计,更常用。

#
★★

35. SQL_MODE=ONLY_FULL_GROUP_BY 的作用(MySQL)?

MySQL 中 SQL_MODE=ONLY_FULL_GROUP_BY 的作用是什么?

  • SQL_MODE 配置
  • 严格分组校验
  • 与标准 SQL 对齐

MySQL 的 ONLY_FULL_GROUP_BY 是一种 SQL_MODE。开启后,MySQL 会拒绝"SELECT 列表中包含既不在 GROUP BY 中、也不是聚合函数的列"的查询,并要求与标准 SQL 一致。关闭时,MySQL 对这类查询不报错,会返回组内任意一行(不确定)的非分组列值,可能导致结果不确定。开启该模式能提升 SQL 正确性、可移植性,避免隐式错误。MySQL 5.7+ 默认开启 ONLY_FULL_GROUP_BY。

ONLY_FULL_GROUP_BY 让 MySQL 严格遵循标准 SQL 的分组规则,防止非分组列被随意选取。默认开启有利于正确性。

#
★★

36. STRING_AGG、ARRAY_AGG、JSON_AGG 的用法差异?

STRING_AGG、ARRAY_AGG、JSON_AGG 的用法差异是什么?

  • 三种聚合的返回类型
  • 分隔符与排序
  • 使用场景

三者都是把分组内多行聚合成一个值:STRING_AGG(col, 'sep') 把多行字符串用分隔符拼接成一个字符串(如 'a,b,c');ARRAY_AGG(col) 把多行聚合成一个数组(如 {a,b,c});JSON_AGG(col) 把多行聚合成一个 JSON 数组(如 [{"id":1},{"id":2}])。三者都支持 ORDER BY 控制组内顺序(如 ARRAY_AGG(col ORDER BY col)),STRING_AGG 可加 DISTINCT。STRING_AGG 返回 text,ARRAY_AGG 返回数组,JSON_AGG 返回 json。

区别在返回类型与用途:字符串拼接、数组、JSON 数组。常用于行转列/聚合展开。

SELECT dept_id,
       string_agg(name, ',') AS names,
       array_agg(name) AS arr,
       json_agg(name) AS js
FROM employee GROUP BY dept_id;
#
★★

37. SUM(NULL) 的返回值?COUNT(NULL) 的返回值?

SUM(NULL) 的返回值?COUNT(NULL) 的返回值?

  • SUM 对 NULL 的处理
  • COUNT 对 NULL 的处理
  • 空集行为

SUM(NULL) 返回 NULL(因为 SUM 忽略 NULL,若所有值都是 NULL 或空集,则返回 NULL)。COUNT(NULL) 返回 0(COUNT(col) 只统计非 NULL 行,col 全为 NULL 时统计 0 行)。注意 COUNT(*) 不处理 NULL,统计所有行。因此:SUM(NULL) = NULL,COUNT(NULL) = 0。这是 NULL 语义的经典陷阱。

关键区别:SUM 忽略 NULL 后空集返回 NULL,COUNT 统计非 NULL 故 NULL 返回 0。两者对 NULL 的处理规则不同。

#
★★

38. 位运算聚合(BIT_AND、BIT_OR、BIT_XOR)的用途?

位运算聚合(BIT_AND、BIT_OR、BIT_XOR)的用途是什么?

  • 位运算聚合函数
  • 逐位 AND/OR/XOR
  • 使用场景

BIT_AND、BIT_OR、BIT_XOR 是位运算聚合函数,对组内所有行的整数按位进行 AND、OR、XOR 运算。BIT_AND 返回所有值按位与的结果(某位为 1 当且仅当所有值该位都为 1),BIT_OR 返回按位或(某位为 1 当任一值该位为 1),BIT_XOR 返回按位异或。常用于权限标志位合并、特征位判断等场景。例如多个权限标志用 BIT_OR 合并,判断用户是否具有任一权限。

位运算聚合把组内多行按位合并。BIT_AND 需全为 1,BIT_OR 任一为 1,BIT_XOR 奇偶校验。

#
★★

39. 布尔聚合函数(BOOL_AND、BOOL_OR)的用法?

布尔聚合函数(BOOL_AND、BOOL_OR)的用法是什么?

  • 布尔聚合
  • ALL/ANY 语义
  • 使用场景

BOOL_AND 和 BOOL_OR 是布尔聚合函数,对组内布尔值做逻辑运算。BOOL_AND 返回 TRUE 当且仅当组内所有值均为 TRUE(即"全部满足"),BOOL_OR 返回 TRUE 当组内任一值为 TRUE(即"存在满足")。NULL 被忽略。常用于判断"是否所有行满足某条件"或"是否存在某条件"。例如 SELECT BOOL_AND(score >= 60) FROM ... 判断是否全部及格。

BOOL_AND 等价于"所有都满足",BOOL_OR 等价于"至少一个满足"。它们是越过 GROUP BY 判断组内整体性质的标准工具。

#
★★

40. 聚合函数的两种实现方式(HashAggregate、GroupAggregate)的适用场景?

聚合函数的两种实现方式(HashAggregate、GroupAggregate)的适用场景是什么?

  • HashAggregate
  • GroupAggregate
  • 适用场景

HashAggregate 通过哈希表按分组键分组聚合,适合输入无序、分组数较多的情况,不需要预排序,但需要足够内存(可能溢出到磁盘)。GroupAggregate 要求输入已按分组键排序,依次扫描相邻组聚合,适合输入已有序(如利用索引)或内存受限的情况,内存使用小但可能有排序代价。当选组较大、数据无序时 HashAggregate 通常更快;当分组键有序(如索引提供)时 GroupAggregate 更合适。

两种聚合实现是排序与哈希的取舍。HashAggregate 免排序但耗内存,GroupAggregate 需有序输入但省内存。优化器根据数据与输入序选择。

#
★★

41. LIKE 的通配符(%、_)能否放在开头?性能影响?

LIKE 的通配符(%、_)能否放在开头?性能影响是什么?

  • 前导通配符
  • 索引失效
  • 全表扫描

通配符可以放在开头(如 LIKE '%abc'),语义合法,但会严重影响性能。因为 % 在开头时,字符串前缀未知,无法使用 B-Tree 索引的前缀匹配,只能全表扫描(或借助 pg_trgm 等特殊索引)。LIKE 'abc%'(通配符在末尾)则可走索引前缀扫描。因此应避免前导通配符;若必须做中间/尾部匹配,可用 pg_trgm 索引或考虑搜索引擎。

前导通配符使索引前缀匹配失效,被迫全表扫描,是 LIKE 性能的经典陷阱。pg_trgm 可缓解但代价高。

#

42. TABLESAMPLE 的 REPEATABLE(seed) 如何保证采样可重现?采样率估计与全表统计相比有何偏差风险?

TABLESAMPLE 的 REPEATABLE(seed) 如何保证采样可重现?采样率估计与全表统计相比有何偏差风险?

  • REPEATABLE 种子
  • 采样可重现
  • 采样偏差

TABLESAMPLE 的 REPEATABLE(seed) 指定随机种子,相同 seed 会生成相同的随机序列,因此在相同条件下(相同表、相同 seed)采样结果可重现。这用于测试或需要确定性的场景。但采样率估计与全表统计相比存在偏差风险:SYSTEM 按块采样可能因数据聚簇(按块聚集)导致样本与整体分布偏差;小样本量下随机波动大;抽样结果对稀有值(如极少数类别)估计不准确。因此抽样统计仅供近似,精确统计需全表扫描。

REPEATABLE 保证确定性,但采样本质是近似,存在抽样误差与聚簇偏差。稀有值估计尤其不可靠。

#

43. AVG(col) 与 SUM(col)/COUNT(col) 的等价性?

AVG(col) 与 SUM(col)/COUNT(col) 的等价性是什么?

  • AVG 的语义
  • 空值处理
  • 数值差异

当 col 不含 NULL 时,AVG(col) 与 SUM(col)/COUNT(col) 等价(AVG 就是总和除以非 NULL 行数)。但若 col 含 NULL,两者在 COUNT 上可能有差异:AVG(col) 忽略 NULL 除以非 NULL 行数,而若写 SUM(col)/COUNT() 则 COUNT() 包含 NULL 行,结果不同。因此正确等价是 AVG(col) = SUM(col) / COUNT(col)(COUNT(col) 只数非 NULL)。整数除法可能产生截断,AVG 返回 numeric 更精确。

AVG(col) 严格等于 SUM(col)/COUNT(col)(非 NULL 计数)。用 COUNT(*) 则不等价。整数除法需注意精度。

#

44. COUNT(*) 与 COUNT(1) 是否等价?

COUNT(*) 与 COUNT(1) 是否等价?

  • COUNT(*) 语义
  • COUNT(1) 语义
  • 性能

COUNT() 与 COUNT(1) 在语义和结果上完全等价,都统计表中所有行数,且都不忽略 NULL(COUNT(1) 的 1 是常量,每行都非 NULL,故统计所有行)。性能上两者也等价,现代数据库优化器会把两者视为相同操作。COUNT(1) 并不比 COUNT() 快。但 COUNT(col) 与 COUNT() 不同,COUNT(col) 忽略 NULL。因此 COUNT() 与 COUNT(1) 等价,COUNT(col) 不与之等价。

COUNT(*) 与 COUNT(1) 等价,都统计总行数。COUNT(col) 才是忽略 NULL 的计数。这是常见面试题。

#

45. 如何统计表的总行数?COUNT(*) 还是 pg_class.reltuples?

如何统计表的总行数?COUNT(*) 还是 pg_class.reltuples?

  • COUNT(*) 精确但慢
  • reltuples 快速但近似
  • 使用场景

统计表总行数有两种方式:COUNT() 做全表扫描,返回精确行数,但数据量大时较慢;pg_class.reltuples 从统计信息读取,速度极快,但只是估算值(由 ANALYZE 更新),可能不精确。若需精确行数(如业务判断)用 COUNT(),若只是估算(如容量规划、优化器参考)用 reltuples。注意 reltuples 在未 ANALYZE 时可能为 -1 或过期。

精确 vs 快速是取舍。COUNT(*) 精确但扫描全表,reltuples 快但近似。生产上按需选择。

#

46. HAVING 子句中的典型用法?

HAVING 子句中的典型用法是什么?

  • HAVING 过滤组
  • 聚合条件
  • 典型场景

HAVING 的典型用法是过滤分组结果,通常包含聚合条件。例如:HAVING COUNT(*) > 10(分组行数超过 10)、HAVING SUM(amount) > 1000(组内金额合计超过 1000)、HAVING MAX(score) >= 90(组的最高分达到某值)。它作用于 GROUP BY 之后的分组,可以对聚合函数结果显示过滤。凡是不能放入 WHERE 的(涉及聚合的)条件,都用 HAVING。

HAVING 用于组的过滤,典型是聚合函数条件。它比 WHERE 晚执行,WHERE 过滤行、HAVING 过滤组。

SELECT dept_id, COUNT(*), SUM(amount)
FROM orders GROUP BY dept_id
HAVING COUNT(*) > 10 AND SUM(amount) > 1000;