1. 概述

使用SQL数据库时,我们可能需要创建视图来表示一个或多个表的数据。视图类型多样,但核心区别在于:视图是仅作为查询的简化封装,还是通过缓存数据来提升访问性能。因此我们区分普通SQL视图(简单或复杂)和物化视图——后者将视图数据作为缓存存储。

本文将通过实际案例,对比普通视图(简单/复杂)与物化视图的差异。

2. 为什么需要视图

视图本质是存储在数据库中的命名SQL语句,相当于展示一个或多个表数据子集的虚拟表。

假设我们有一个需要频繁复用或暴露给Web应用的查询。通过视图,我们可以为这个查询命名并存储在数据库中。

视图能提升语句可读性,特别是当查询涉及跨库表连接时。从安全角度,我们可以通过视图权限控制隐藏敏感信息(如薪资字段)。

此外,我们还会看到物化视图如何实现查询性能扩展。

先看这个常用子查询语法:

SELECT * FROM
(
SELECT
column1,
column2
FROM
some_table
) s;

这种子查询可称为内联视图。所有视图都基于相同概念,但额外使用DDL命令将查询存储为可复用语句。

3. 数据库示例

PostgreSQL为例,创建员工-部门测试数据库。首先定义包含id和name的部门表:

CREATE TABLE department (
    department_id int8 NOT NULL,
    department_name varchar(255) NOT NULL,
    CONSTRAINT department_id_pkey PRIMARY KEY (department_id)
);

再定义员工表,包含部门外键:

CREATE TABLE employee (
    employee_id int8 NOT NULL,
    employee_name varchar(255) NOT NULL,
    department_id int8 NOT NULL,
    salary int8 NOT NULL,
    CONSTRAINT empolyee_id_pkey PRIMARY KEY (empolyee_id),
    CONSTRAINT fk_department_department_id FOREIGN KEY (department_id) REFERENCES department(department_id)
);

4. 普通视图与复杂视图

当查询资源消耗低且结果频繁变化时,适合使用普通或复杂视图。

4.1. 普通视图

普通视图仅基于单个基表,即只从一张表获取数据。

假设需通过Web应用展示员工信息,但需隐藏薪资字段。我们可以创建只显示id和name的视图,并对非管理员用户授予该视图权限,隐藏原始表。

创建普通视图:

CREATE VIEW employee_details AS
SELECT
employee_id,
employee_name
FROM employee;

创建后可像表一样查询:

SELECT employee_id, employee_name
FROM employee_details;

输出示例: DB Simple View

4.2. 复杂视图

复杂视图包含多个基表,且可能使用GROUP BY、DISTINCT、表达式计算列或聚合函数。

先创建连接部门表的视图:

CREATE VIEW employee_department AS
SELECT
e.employee_id,
e.employee_name,
d.department_name
FROM employee e
INNER JOIN department d ON e.department_id = d.department_id;

同样可查询:

SELECT employee_name, department_name
FROM employee_department;

结果集示例: DB Complex View

再看更复杂的聚合视图——按部门统计薪资:

CREATE VIEW department_salary AS
SELECT
d.department_id,
d.department_name,
COUNT(e.*) AS employee_count,
SUM(e.salary) AS total_salary
FROM department d
INNER JOIN employee e ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name;

查询结果示例: DB Complex View 2

由于视图可视为表,可对其添加WHERE条件:

SELECT department_id,
department_name,
employee_count,
total_salary
FROM department_salary
WHERE department_id = 2;

4.3. 视图上的DML操作

虽然不推荐,但可通过视图执行DML操作(INSERT/DELETE/UPDATE)。例如更新员工姓名:

UPDATE employee_details
SET employee_name = 'Eric Johnson'
WHERE employee_id = 6;

⚠️ 仅限单表普通视图支持DML,复杂视图无法直接执行。

5. 物化视图

物化视图直接存储查询结果集。当面对包含子查询、连接和聚合的昂贵查询时,保存结果集可显著提升性能。因此常用于需要常量时间访问的大数据集。

不同数据库对物化视图支持差异很大:

  • PostgreSQL:提供DDL命令,但需手动刷新数据
  • Oracle:面向数据仓库设计,支持定时/提交刷新等高级功能(通过物化视图日志实现增量刷新)
  • MySQL:无内置支持,需借助LeapDB等插件

以PostgreSQL为例,创建部门薪资统计的物化视图:

CREATE MATERIALIZED VIEW materialized_department_salary AS
SELECT d.department_id, 
d.department_name, 
COUNT(e.*) AS employee_count, 
SUM(e.salary) AS total_salary 
FROM department d INNER JOIN employee e ON d.department_id = e.department_id 
GROUP BY d.department_id, d.department_name;

可带条件查询:

SELECT * 
FROM materialized_department_salary
WHERE department_id = 5;

结果示例: DB Materialized View

关键点:物化视图本质是表,可创建索引或分区提升性能。例如添加索引:

CREATE UNIQUE INDEX ON materialized_department_salary (
   department_id,
   employee_count,
   total_salary
);

6. 普通视图与物化视图的核心差异

特性 普通视图(简单/复杂) 物化视图
数据存储 仅存储查询逻辑 物理存储结果集
数据时效性 实时(每次查询重新执行) 静态快照(需手动/定时刷新)
性能影响 查询复杂度决定性能 预计算数据,访问速度快
适用场景 ✅ 安全控制
✅ 查询简化
✅ 大数据分析
✅ 离线访问
DML操作 简单视图支持 通常不支持(需特殊配置)
扩展能力 ❌ 不可索引/分区 ✅ 支持索引/分区

简单粗暴总结

  • 普通视图:安全隔离的"虚拟表",每次查询都重新计算
  • 物化视图:性能优化的"数据缓存",牺牲实时性换取速度

⚠️ 随着NoSQL(如键值存储)兴起,传统视图使用率下降,但物化视图仍在数据分析领域广泛应用。

7. 结论

本文对比了普通视图与物化视图的核心差异:

  • 普通视图(简单/复杂):每次访问都重新执行查询,主要用于安全控制和查询简化
  • 物化视图:存储物理数据快照,通过预计算提升性能,适合大数据分析场景

物化视图可应用索引和分区优化,而普通视图则更侧重访问控制。在选择时需权衡实时性与性能需求——普通视图保证数据最新但可能较慢,物化视图访问飞快但数据可能非最新。

踩坑提醒:MySQL用户需注意其原生不支持物化视图,Oracle/PostgreSQL则提供更完善的实现方案。


原始标题:Databases: Simple vs. Complex vs. Materialized Views