目录

第一章 子查询基本概念

1.1 什么是子查询

1.2 为什么需要子查询

1.3 子查询分类(按返回形态)

1.3.1 按使用位置分类

1.3.2 记忆口诀

第二章 证券行业案例:数据表设计

第三章 子查询实战案例(证券业务)

3.1 标量子查询(单个值)

3.2 多行子查询 + IN

3.3 存在性子查询 EXISTS

3.4 相关子查询

3.5 ALL / ANY 子查询

3.6 派生表(FROM 子查询)

3.7 HAVING + 子查询

3.8 LATERAL 子查询(扩展内容)

3.9 递归 CTE (WITH RECURSIVE) 扩展内容

第四章 WITH (CTE) 临时命名查询

4.1 CTE 是什么

4.2 基本语法

4.3 单 CTE 示例

4.4 多 CTE 示例(链式引用)

4.5 CTE vs 派生表 vs 临时表

第五章 视图

5.1 什么是视图

5.2 为什么需要视图

5.3 视图 vs CTE vs 临时表

5.4 创建视图(基础语法)

5.5 证券行业视图示例

5.6 修改和删除视图

5.7 可更新视图与 CHECK OPTION

5.8 物化视图(性能优化)

第六章 临时表(TEMP TABLE)

6.1 为什么需要临时表

6.2 生命周期与可见性

6.3 基本语法

6.4 事务级临时表

6.5 查看和删除临时表

6.6 性能最佳实践

第七章 总结

7.1 子查询口诀

7.2 CTE 与视图选择指南


第一章 子查询基本概念

1.1 什么是子查询

子查询 就是在一个 SQL 语句中嵌套另一个 SELECT 语句。执行时,数据库会先执行内层的子查询,将结果返回给外层主查询使用。

零基础解释:就像你查东西要先查一个中间结果。比如“查询交易额高于平均交易额的客户”,你需要先算出“平均交易额”这个中间值,然后再用它去比较。这个中间值就可以通过子查询获得。

1.2 为什么需要子查询

  • 数据无法直接获取:需要的数据不能直接拿到,必须通过查询计算后才能得到

  • 分步思考:把复杂问题拆解成多个查询步骤,逻辑更清晰

  • 动态条件:条件值不是固定的,而是依赖于表中其他数据

1.3 子查询分类(按返回形态)

类别

返回内容

常用运算符

记忆示例

标量子查询

1 行 1 列(单个值)

=, >, <, >=, <=, <>

WHERE salary > (SELECT AVG(salary) FROM employees)

多行子查询

N 行 1 列(一列值列表)

IN, ANY, ALL

WHERE dept_id IN (SELECT dept_id FROM ...)

多列子查询

N 行 N 列(多列组合)

IN, EXISTS

WHERE (dept, job) IN (SELECT dept, job FROM ...)

存在性子查询

布尔值(是否至少一行)

EXISTS, NOT EXISTS

WHERE EXISTS (SELECT 1 FROM ...)

派生表子查询

多行多列(临时表)

FROM 子句

SELECT * FROM (SELECT ...) AS t

1.3.1 按使用位置分类

位置

用途

子查询类型

WHERE 子句

过滤行

标量、多行、存在性

FROM 子句

生成临时表(派生表)

派生表

SELECT 子句

计算列(标量值)

标量子查询

HAVING 子句

分组后过滤

标量子查询

1.3.2 记忆口诀

“标多存派”——标量、多行、存在性、派生表,四种子查询类型

第二章 证券行业案例:数据表设计

为了演示子查询和视图,我们创建一套证券交易业务的数据模型。

-- 创建证券业务 schema
CREATE SCHEMA IF NOT EXISTS securities;

-- 1. 客户表 (clients)
CREATE TABLE securities.clients (
    client_id     INTEGER PRIMARY KEY,      -- 客户编号
    full_name     VARCHAR(50) NOT NULL,     -- 客户姓名
    id_card       VARCHAR(18) UNIQUE,       -- 身份证号
    phone         VARCHAR(20),              -- 联系电话
    risk_level    VARCHAR(10) DEFAULT '平衡型', -- 风险等级:保守型/平衡型/进取型
    open_date     DATE DEFAULT CURRENT_DATE
);

-- 2. 证券公司/营业部表 (brokerages)
CREATE TABLE securities.brokerages (
    brokerage_id   INTEGER PRIMARY KEY,     -- 券商编号
    brokerage_name VARCHAR(50) NOT NULL,    -- 券商名称
    city           VARCHAR(30)              -- 所在城市
);

-- 3. 证券账户表 (accounts)
CREATE TABLE securities.accounts (
    account_id     INTEGER PRIMARY KEY,     -- 资金账户号
    client_id      INTEGER NOT NULL,        -- 所属客户编号
    brokerage_id   INTEGER NOT NULL,        -- 开户券商编号
    account_type   VARCHAR(20) DEFAULT '普通账户', -- 普通账户/两融账户
    balance        NUMERIC(12,2) DEFAULT 0.00,    -- 资金余额(元)
    frozen_amount  NUMERIC(12,2) DEFAULT 0.00,    -- 冻结金额
    status         VARCHAR(10) DEFAULT '正常',   -- 正常/冻结/销户
    FOREIGN KEY (client_id) REFERENCES securities.clients(client_id),
    FOREIGN KEY (brokerage_id) REFERENCES securities.brokerages(brokerage_id)
);

-- 4. 股票信息表 (stocks)
CREATE TABLE securities.stocks (
    stock_code     VARCHAR(10) PRIMARY KEY, -- 股票代码(如 '000001')
    stock_name     VARCHAR(30) NOT NULL,    -- 股票名称
    industry       VARCHAR(30),             -- 所属行业
    list_date      DATE                     -- 上市日期
);

-- 5. 交易记录表 (transactions)
CREATE TABLE securities.transactions (
    trans_id       INTEGER PRIMARY KEY,
    account_id     INTEGER NOT NULL,        -- 交易账户
    stock_code     VARCHAR(10) NOT NULL,    -- 股票代码
    trans_type     VARCHAR(4) NOT NULL,     -- 买入/卖出
    quantity       INTEGER NOT NULL,        -- 成交数量(股)
    price          NUMERIC(10,2) NOT NULL,  -- 成交价格(元/股)
    trans_time     TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    commission     NUMERIC(10,2),           -- 佣金
    FOREIGN KEY (account_id) REFERENCES securities.accounts(account_id),
    FOREIGN KEY (stock_code) REFERENCES securities.stocks(stock_code)
);

-- 6. 每日持仓快照表(用于演示聚合子查询)
CREATE TABLE securities.daily_positions (
    id             INTEGER PRIMARY KEY,
    account_id     INTEGER NOT NULL,
    stock_code     VARCHAR(10) NOT NULL,
    hold_quantity  INTEGER NOT NULL,        -- 持仓数量
    snapshot_date  DATE DEFAULT CURRENT_DATE,
    FOREIGN KEY (account_id) REFERENCES securities.accounts(account_id)
);

-- 插入券商数据
INSERT INTO securities.brokerages VALUES (1, '中信证券', '北京');
INSERT INTO securities.brokerages VALUES (2, '华泰证券', '上海');
INSERT INTO securities.brokerages VALUES (3, '国泰君安', '深圳');

-- 插入客户数据
INSERT INTO securities.clients VALUES (101, '张三', '1101011XXXX001011234', '13800001111', '进取型', '2020-01-10');
INSERT INTO securities.clients VALUES (102, '李四', '310101XXXX001023j456', '13912345678', '平衡型', '2021-03-15');
INSERT INTO securities.clients VALUES (103, '王芳', '440301XXXX0010h36789', '13687654321', '保守型', '2019-11-20');
INSERT INTO securities.clients VALUES (104, '赵雷', '510101XXXX00104j8888', '15900000000', '进取型', '2023-05-01');
INSERT INTO securities.clients VALUES (105, '孙梅', '4201061XXXX001105555', '17711112222', '平衡型', '2022-08-08');

-- 插入证券账户
INSERT INTO securities.accounts VALUES (1001, 101, 1, '普通账户', 500000.00, 0, '正常');
INSERT INTO securities.accounts VALUES (1002, 101, 1, '两融账户', 200000.00, 0, '正常');
INSERT INTO securities.accounts VALUES (1003, 102, 2, '普通账户', 80000.00, 0, '正常');
INSERT INTO securities.accounts VALUES (1004, 103, 3, '普通账户', 2000000.00, 0, '正常');
INSERT INTO securities.accounts VALUES (1005, 103, 3, '两融账户', 500000.00, 0, '正常');
INSERT INTO securities.accounts VALUES (1006, 104, 2, '普通账户', 10000.00, 0, '冻结');
-- 客户105(孙梅)无账户,用于演示外连接

-- 插入股票信息
INSERT INTO securities.stocks VALUES ('000001', '平安银行', '银行', '1991-04-03');
INSERT INTO securities.stocks VALUES ('600519', '贵州茅台', '白酒', '2001-08-27');
INSERT INTO securities.stocks VALUES ('000858', '五粮液', '白酒', '1998-04-27');
INSERT INTO securities.stocks VALUES ('300750', '宁德时代', '新能源', '2018-06-11');

-- 插入交易记录
INSERT INTO securities.transactions VALUES (5001, 1001, '600519', '买入', 100, 1800.00, '2025-01-10 09:30:00', 50.00);
INSERT INTO securities.transactions VALUES (5002, 1001, '600519', '买入', 100, 1850.00, '2025-01-15 14:20:00', 50.00);
INSERT INTO securities.transactions VALUES (5003, 1003, '000001', '买入', 1000, 12.50, '2025-02-01 11:00:00', 10.00);
INSERT INTO securities.transactions VALUES (5004, 1004, '300750', '买入', 500, 200.00, '2025-02-10 16:00:00', 100.00);
INSERT INTO securities.transactions VALUES (5005, 1004, '300750', '卖出', 200, 210.00, '2025-02-14 19:30:00', 42.00);
INSERT INTO securities.transactions VALUES (5006, 1005, '000858', '买入', 300, 150.00, '2025-02-18 10:00:00', 30.00);
INSERT INTO securities.transactions VALUES (5007, 1001, '000001', '买入', 2000, 11.80, '2025-03-01 13:00:00', 20.00);

-- 插入每日持仓快照
INSERT INTO securities.daily_positions VALUES (1, 1001, '600519', 200, '2025-03-31');
INSERT INTO securities.daily_positions VALUES (2, 1001, '000001', 2000, '2025-03-31');
INSERT INTO securities.daily_positions VALUES (3, 1003, '000001', 1000, '2025-03-31');
INSERT INTO securities.daily_positions VALUES (4, 1004, '300750', 300, '2025-03-31');
INSERT INTO securities.daily_positions VALUES (5, 1005, '000858', 300, '2025-03-31');

第三章 子查询实战案例(证券业务)

3.1 标量子查询(单个值)

场景:查询交易金额高于平均交易金额的交易记录。

-- 查询单笔成交金额超过所有交易平均金额的交易
SELECT trans_id
      ,account_id
      ,stock_code
      ,quantity * price AS turnover
FROM securities.transactions
WHERE quantity * price > (SELECT AVG(quantity * price) FROM securities.transactions);

3.2 多行子查询 + IN

场景:查询在“中信证券”开户的客户姓名。

SELECT full_name
FROM securities.clients
WHERE client_id IN (
    SELECT client_id
    FROM securities.accounts
    WHERE brokerage_id = (SELECT brokerage_id FROM securities.brokerages WHERE brokerage_name = '中信证券')
);

3.3 存在性子查询 EXISTS

场景:查询发生过交易(买入或卖出)的所有客户。

SELECT c.full_name
FROM securities.clients c
WHERE EXISTS (
    SELECT 1
    FROM securities.accounts a
    JOIN securities.transactions t ON a.account_id = t.account_id
    WHERE a.client_id = c.client_id
);

3.4 相关子查询

场景:查询交易金额高于自己账户平均交易金额的交易记录(相关子查询)。

SELECT t1.trans_id
      ,t1.account_id
      ,t1.stock_code
      ,t1.quantity * t1.price AS turnover
FROM securities.transactions t1
WHERE t1.quantity * t1.price > (
    SELECT AVG(t2.quantity * t2.price)
    FROM securities.transactions t2
    WHERE t2.account_id = t1.account_id   -- 关联到外层账户
);

3.5 ALL / ANY 子查询

场景:查询交易金额高于任意一笔贵州茅台(600519)交易的交易记录(即比茅台的最小成交额高即可)。

SELECT trans_id
      ,account_id
      ,quantity * price AS turnover
FROM securities.transactions
WHERE quantity * price > ANY (
    SELECT quantity * price
    FROM securities.transactions
    WHERE stock_code = '600519'
);

3.6 派生表(FROM 子查询)

场景:查询每个账户的总交易金额,并只显示总交易金额高于 50000 的账户。

SELECT account_id
      ,total_turnover
FROM (
    SELECT account_id
          ,SUM(quantity * price) AS total_turnover
    FROM securities.transactions
    GROUP BY account_id
    ) AS account_summary
WHERE total_turnover > 50000;

3.7 HAVING + 子查询

场景:查询平均交易金额高于全公司平均交易金额的账户。

SELECT account_id
      ,AVG(quantity * price) AS avg_turnover
FROM securities.transactions
GROUP BY account_id
HAVING AVG(quantity * price) > (SELECT AVG(quantity * price) FROM securities.transactions);

3.8 LATERAL 子查询(扩展内容)

LATERAL 子查询允许子查询引用它同一 FROM 子句中出现在它前面的表的列,相当于为外层查询的每一行都执行一次子查询。它的核心价值在于解决“派生表无法引用外层列”的限制:例如要查询“每个客户最近的一笔交易”,普通子查询很难做到,但用 LATERAL 可以先取客户,然后对每个客户执行 ORDER BY trans_time LIMIT 1 的子查询,从而为每个客户动态计算独立结果。简单理解:LATERAL 让子查询不再是静态的独立查询,而是能跟随主查询每一行的“随行查询”。

场景:为每个客户查询其最近的一次交易。

SELECT c.full_name
      ,latest.*
FROM securities.clients c
LEFT JOIN LATERAL (
    SELECT t.trans_id
          ,t.stock_code
          ,t.quantity * t.price AS turnover
    FROM securities.accounts a
    inner JOIN securities.transactions t 
            ON a.account_id = t.account_id
    where a.client_id = c.client_id
    ORDER BY t.trans_time DESC
    LIMIT 1
) latest ON true;

3.9 递归 CTE (WITH RECURSIVE) 扩展内容

递归 CTE 常用于遍历树形结构(如组织架构、产品分类)。在证券行业中,可以用于查找“同一控制人下的所有账户”。

-- 假设有一个控制关系表(非实际业务,仅演示语法)
-- 查找某一客户控制的所有关联账户
WITH RECURSIVE related_accounts AS (
    -- 起点
    SELECT a.account_id, a.client_id
    FROM securities.accounts a
    WHERE a.client_id = 101
    UNION
    -- 递归部分:通过某种关系继续寻找(此处为简化示例)
    SELECT a.account_id, a.client_id
    FROM securities.accounts a
    JOIN related_accounts r ON a.client_id = r.client_id  -- 实际需要业务关系字段
)
SELECT * FROM related_accounts;

第四章 WITH (CTE) 临时命名查询

4.1 CTE 是什么

CTE (Common Table Expression) 官方名称为“公共表表达式”,俗称 “WITH 虚拟视图”“一次性的临时表”

  • 作用:把复杂的 SELECT 片段起个名字,后面直接当表用

  • 生命周期:只存在当前查询,运行结束自动消失

  • 优点:

    • 逻辑分层,易读易维护

    • 可以递归(遍历树/图)

    • 避免重复嵌套,提高可读性

4.2 基本语法

WITH 临时表名 AS (
    SELECT 列1, 列2, ...
    FROM 表名
    WHERE 条件
)
SELECT * FROM 临时表名;

执行顺序:先执行 WITH 里面的 SELECT,再执行外面的 SELECT

4.3 单 CTE 示例

场景:使用 CTE 查询交易金额高于平均值的交易。

WITH avg_turnover AS (
    SELECT AVG(quantity * price) AS avg_val
    FROM securities.transactions
)
SELECT trans_id
      ,account_id
      ,quantity * price AS turnover
FROM securities.transactions, avg_turnover
WHERE quantity * price > avg_turnover.avg_val;

4.4 多 CTE 示例(链式引用)

场景:先计算每个账户的交易总额,再计算每个账户与账户平均值的差额。

WITH account_total AS (
    SELECT account_id
          ,SUM(quantity * price) AS total_turnover
    FROM securities.transactions
    GROUP BY account_id
),
overall_avg AS (
    SELECT AVG(total_turnover) AS avg_turnover
    FROM account_total
)
SELECT account_id
      ,total_turnover
      ,total_turnover - avg_turnover AS diff
FROM account_total, overall_avg
ORDER BY diff DESC;

4.5 CTE vs 派生表 vs 临时表

维度

CTE (WITH)

派生表 (FROM 子查询)

临时表 (TEMP TABLE)

生命周期

当前语句

当前语句

会话/事务

是否存真实数据

❌ 不存储

❌ 不存储

✅ 存储

可递归

✅ 支持

❌ 不支持

❌ 不支持

多次引用

⚠️ 每次重新计算

✅ 可复用

适用场景

分层逻辑、递归

简单子查询

中间结果复用

性能贴士:在 PostgreSQL 中,同一 SQL 里多次引用同一 CTE,会重新计算多次。大数据量时建议改用临时表或物化视图。

第五章 视图

5.1 什么是视图

视图(View) 是一条保存下来的 SELECT 语句。可以把它理解为给复杂查询起一个永久的名字,之后可以像普通表一样查询。

  • 不存储真实数据,只存储查询定义(逻辑表)

  • 视图的数据来源于基表(创建视图时依赖的表)

  • 对视图执行 DML 操作(增删改)实际上会影响基表(受限于视图规则)

5.2 为什么需要视图

  • 简化复杂 SQL:把多表关联封装成视图,用户直接 SELECT * FROM 视图

  • 统一口径:确保所有人用同一个统计规则

  • 屏蔽敏感字段:隐藏工资、身份证号等敏感列

  • 兼容旧程序:底层表结构调整后,通过视图保持原接口不变

5.3 视图 vs CTE vs 临时表

维度

普通视图

CTE (WITH)

临时表

生命周期

永久

当前语句

会话/事务

是否存真实数据

可更新(DML)

✅(简单视图)

递归能力

口诀:“封装用视图,计算用临时,分层/树用 WITH”

5.4 创建视图(基础语法)

CREATE VIEW 视图名 AS
SELECT 列1, 列2, ...
FROM 表名
WHERE 条件;

5.5 证券行业视图示例

示例 1:简化复杂查询

场景:创建视图封装“账户-客户-券商”四表关联,后续直接使用。

CREATE VIEW securities.v_account_full AS
SELECT a.account_id
      ,c.full_name AS client_name
      ,c.risk_level
      ,b.brokerage_name
      ,a.balance
      ,a.status
FROM securities.accounts a
inner JOIN securities.clients c 
        ON a.client_id = c.client_id
inner JOIN securities.brokerages b 
        ON a.brokerage_id = b.brokerage_id;

-- 使用视图:像普通表一样查询
SELECT * 
FROM securities.v_account_full 
WHERE balance > 100000;

示例 2:屏蔽敏感字段

场景:对外提供客户信息视图,隐藏身份证号、手机号。

CREATE VIEW securities.v_clients_public AS
SELECT client_id
      ,full_name
      ,risk_level
      ,open_date
FROM securities.clients;

示例 3:统一统计口径

场景:创建部门/券商维度的统计报表视图。

CREATE VIEW securities.v_brokerage_stats AS
SELECT b.brokerage_name
      ,COUNT(DISTINCT a.client_id) AS client_count
      ,COUNT(a.account_id)         AS account_count
      ,SUM(a.balance)              AS total_balance
      ,AVG(a.balance)              AS avg_balance
FROM securities.brokerages b
LEFT JOIN securities.accounts a 
       ON b.brokerage_id = a.brokerage_id
GROUP BY b.brokerage_id, b.brokerage_name;

5.6 修改和删除视图

-- 替换视图(覆盖定义)
CREATE OR REPLACE VIEW securities.v_clients_public AS
SELECT client_id
      ,full_name
      ,risk_level  -- 去掉了 open_date
FROM securities.clients;

-- 删除视图
DROP VIEW IF EXISTS securities.v_clients_public;

5.7 可更新视图与 CHECK OPTION

简单视图(基于单表,不包含聚合、DISTINCT、GROUP BY 等)支持 DML 操作。

-- 重建视图,包含所有 NOT NULL 字段
CREATE OR REPLACE VIEW securities.v_active_accounts AS
SELECT 
    account_id, 
    client_id, 
    brokerage_id,    -- ✅ 加上这个
    account_type,    -- ✅ 如果有默认值,可加可不加
    balance, 
    status
FROM securities.accounts
WHERE status = '正常'
WITH CHECK OPTION;   -- 防止插入/更新后不满足视图条件

-- 插入时需要提供 brokerage_id
INSERT INTO securities.v_active_accounts 
(account_id, client_id, brokerage_id, balance, status)
VALUES (2001, 101, 1, 10000, '正常');  -- brokerage_id=1 对应中信证券

-- 以下插入会失败,因为 status='冻结' 不满足视图条件(被 CHECK OPTION 拦截)
INSERT INTO securities.v_active_accounts (account_id, client_id, balance, status)
VALUES (2002, 102,2, 5000, '冻结');

注意:通过视图插入数据,会将数据插入到基表中。

5.8 物化视图(性能优化)

物化视图Materialized View真正存储数据,适合报表类场景,但需要手动刷新。

-- 创建物化视图
CREATE MATERIALIZED VIEW securities.mv_daily_position_summary AS
SELECT snapshot_date
      ,stock_code
      ,SUM(hold_quantity) AS total_hold
      ,COUNT(DISTINCT account_id) AS holder_count
FROM securities.daily_positions
GROUP BY snapshot_date, stock_code;

-- 刷新物化视图(全量刷新)
REFRESH MATERIALIZED VIEW securities.mv_daily_position_summary;

-- 使用物化视图
SELECT * FROM securities.mv_daily_position_summary;

注意:物化视图刷新前数据不会变,适合对实时性要求不高的报表场景。PostgreSQL 还支持 REFRESH MATERIALIZED VIEW CONCURRENTLY 并发刷新,但需要唯一索引。

第六章 临时表(TEMP TABLE)

6.1 为什么需要临时表

  • 中间结果存储:多步复杂报表,避免反复扫描大表

  • 会话隔离:不同连接互不干扰,天然“线程私有”

  • 自动清理:会话或事务结束自动回收

  • 降低锁竞争:将热点操作转移到临时表,减少主表锁等待

6.2 生命周期与可见性

模式

关键字

生命周期

可见范围

会话级(默认)

TEMP / TEMPORARY

会话结束

仅当前会话

事务级

ON COMMIT DROP

事务结束

仅当前事务

6.3 基本语法

-- 最简形式
CREATE TEMP TABLE tmp_stock_list (stock_code VARCHAR(10), hold_quantity INTEGER);

-- 带约束和索引
CREATE TEMP TABLE tmp_trade_summary (
    account_id INTEGER PRIMARY KEY,
    total_amount NUMERIC(12,2) NOT NULL,
    trade_count INTEGER
);
CREATE INDEX tmp_idx_account ON tmp_trade_summary(account_id);

-- 从查询结果直接创建临时表
SELECT account_id
      ,SUM(quantity * price) AS total_turnover
INTO TEMP TABLE tmp_account_turnover
FROM securities.transactions
GROUP BY account_id;

-- 查询临时结果表数据
select * from tmp_account_turnover;

6.4 事务级临时表

BEGIN;
CREATE TEMP TABLE tmp_tx (id INT) ON COMMIT DROP;
-- 在事务内使用临时表
INSERT INTO tmp_tx VALUES (1);
COMMIT;  -- 事务结束后临时表自动删除

6.5 查看和删除临时表

-- 查看当前会话所有临时表
SELECT schemaname
      ,tablename
FROM pg_tables
WHERE schemaname LIKE 'pg_temp%';

-- 手动删除
DROP TABLE IF EXISTS tmp_stock_list;

6.6 性能最佳实践

  • 命名规范:统一前缀 tmp_,一眼识别

  • 连接池友好:使用 ON COMMIT DROP 或手动 DROP,避免临时表在长连接中累积

  • 大数据量写入前:可设置 ALTER TABLE tmp SET UNLOGGED; 减少 WAL 日志压力

  • 手动分析:临时表不会被 AUTOVACUUM 扫描,频繁更新后应手动 ANALYZE tmp_table

  • 内存配置:合理设置 temp_buffers(默认 8MB,可调大减少磁盘 I/O)

第七章 总结

7.1 子查询口诀

“单行用比较,多行用集合”

“标多存派,位置不同”

子查询类型

返回形态

常用位置

典型运算符

标量子查询

1 值

WHERE/SELECT/HAVING

=, >, <

多行子查询

1 列多行

WHERE

IN, ANY, ALL

存在性子查询

布尔值

WHERE

EXISTS, NOT EXISTS

派生表

多行多列

FROM

7.2 CTE 与视图选择指南

需求

推荐方案

复杂查询需要分层阅读

WITH (CTE)

需要递归遍历(树形结构)

WITH RECURSIVE

需要永久封装、复用

普通视图

需要屏蔽敏感字段

普通视图

需要加速报表查询

物化视图

需要存储中间结果、多步骤复用

临时表

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐