一、前置回顾:数据库安全性与完整性
(一)存取控制
- 核心目标:仅允许用户访问或修改其有权操作的数据
- 关键语句:
- 授权:
Grant privs On R to users [With Grant Option] - 撤销授权:
Revoke privs On R From users [Cascade | Restrict]
- 授权:
- 支持授权关系可视化(Grant diagram)
(二)数据完整性
1. 静态完整性约束
- 基础约束类型:
Primary Key(主键)、Foreign Key(外键)、Unique(唯一)、NOT NULL(非空)、Check(自定义检查) - 核心作用:防止数据录入错误(inserts)、保证更新操作正确性(updates)、维护数据一致性、为数据库存储和查询提供数据规则
2. 动态完整性(触发器)
-
核心逻辑:
When event occurs, check condition; if true, do action(当事件触发时,检查条件,满足则执行操作) -
作用:将业务逻辑从应用程序迁移到 DBMS,增强约束表达能力,支持约束 "修复" 逻辑
-
SQL 标准语法:
sqlCreate Trigger name Before | After | Instead Of events [referencing-variables] [For Each Row] When (condition) action -
特殊应用:通过
Instead of触发器实现视图的自定义修改
(三)视图相关
1. 普通视图作用
- 数据隐藏:对不同用户隐藏敏感数据
- 简化查询:使复杂查询更简洁、更符合自然逻辑
- 访问模块化:规范数据库访问方式
2. 物化视图(Materialized Views)
- 继承普通视图全部作用
- 额外优势:类似索引,可显著提高查询效率
3. SQL 标准可更新视图(updatable views)定义
- 基于单个表 T 的查询(无
Distinct) - 视图中未包含的属性需允许为
NULL或有默认值 - 子查询不得引用表 T
- 无
Group by或聚合函数
二、PostgreSQL 扩展(11.1 节)
(一)PostgreSQL 服务器架构
- 逻辑功能划分:三层结构(客户机→应用服务器→数据库服务器)
- 核心优势:
- 性能优化:数据直接在数据库内部访问,无需跨端传输
- 易于维护:直接更新数据库服务器,无需修改客户端
- 安全可控:仅授权用户访问函数,无法直接操作底层表
(二)PL/pgSQL 语言
1. 语言特性
- 设计渊源:受 Oracle 的 PL/SQL 影响
- 功能定位:功能强大的 SQL 脚本语言,虽 PostgreSQL 未明确称其为 "存储过程",但通过完整控制结构 + 触发器 + 运算符 + 索引支持,形成完整存储过程开发系统
- 核心优点:易于上手、默认集成于大多数 PostgreSQL 部署、针对数据密集型任务优化性能
2. 语法规则
- 块结构:
[ <<label>> ] [ DECLARE declarations ] BEGIN statements END [label]; - 结束符要求:变量申明和语句以分号
;结尾,嵌套 block 的 END 后需加;(最后一个 END 除外) - 注释方式:单行注释
--,多行注释/* */ - 大小写规则:变量和关键词不区分大小写(字符 'A' 和字符串 "A" 除外)
- 变量作用域:Block 内部可声明变量,同名变量覆盖外部变量,可通过
label.variable访问外部变量 - 变量声明语法:
name [CONSTANT] type [COLLATE collation_name] [NOT NULL] [{DEFAULT | := | =} expression];
(三)自定义类型和操作符
1. 自定义类型创建
- 语法示例(水果数量类型):
CREATE TYPE FRUIT_QTY as (name text, qty int); - 类型转换:使用
::符号,如'Point(10 20)'::geometry、'(''APPLE'', 3)'::FRUIT_QTY
2. 自定义函数与操作符绑定
-
步骤:先创建自定义比较函数,再将函数绑定为操作符
-
示例(水果价值比较):
sql-- 自定义比较函数 CREATE FUNCTION fruity_qty_larger_than(left_fruit FRUIT_QTY, right_fruit FRUIT_QTY) RETURNS BOOL AS $$ ... $$ LANGUAGE plpgsql; -- 绑定为>操作符 CREATE OPERATOR > (leftarg = FRUIT_QTY, rightarg = FRUIT_QTY, procedure = fruit_qty_larger_than, commutator = >);
3. 扩展几何类型示例
sql
Create Type Geometry As Object (
Private Dimension SmallInt Default -1,
Private CoordinateDimension SmallInt Default 2,
Private Is3D SmallInt Default 3,
Private IsMeasured SmallInt Default 0)
Not Instantiable
Not Final
- 支持自定义方法(如
Dimension()函数)和几何操作符(如&&:判断 2D 包围盒是否相交)
(四)定制排序方法和索引
1. 核心逻辑
- 基于自定义函数实现特殊排序规则,再为该函数创建索引,优化查询性能
2. 示例(按元音逆向排序)
sql
-- 自定义排序函数
CREATE OR REPLACE FUNCTION reversed_vowels(word text) RETURNS text AS $$
vowels = [c for c in word.lower() if c in 'aeiou']
vowels.reverse();
return ''.join(vowels);
$$ LANGUAGE plpythonu IMMUTABLE;
-- 使用排序函数查询
Select word, reversed_vowels(word) from words order by reversed_vowels(word);
-- 创建索引优化性能
CREATE INDEX reversed_vowels_index ON words (reversed_vowels(word));
三、函数(11.2 节)
(一)函数结构
1. 核心元素
-
组成部分:函数名、参数(变量名 + 类型名)、返回类型、函数主体、语言声明
-
语法示例(笛卡尔距离计算):
sqlcreate or replace function ST_P2PDistance(x1 float, y1 float, x2 float, y2 float) returns float as $$ begin return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); end; $$ language plpgsql;
2. 变量与数据访问
- 变量声明:除参数外,所有变量需在
DECLARE块中提前声明,仅在函数执行的 block 内有效 - 数据赋值:支持
SELECT select_expressions INTO target FROM ...语法(如转账函数中查询余额赋值) - 函数重载:支持同名函数不同参数列表(返回类型不同不构成重载),如
mid(str varchar, start integer)与mid(str varchar, start integer, end integer)
(二)条件表达式
1. IF/THEN/ELSE 语句
sql
IF boolean-expression THEN
statements
[ ELSIF boolean-expression THEN
statements ...]
[ ELSE
statements ]
END IF;
- 注意:赋值操作(
= | :=)与相等判断(=)的语法区别 - 简化示例:
IF(trim(firstname) = '', NULL, firstname)
2. CASE 语句
sql
CASE search-expression
WHEN expression [, expression [ ... ]] THEN
statements
[ WHEN expression [, expression [ ... ]] THEN
statements ... ]
[ ELSE
statements ]
END CASE;
-
示例:
sqlCASE x WHEN 1, 2 THEN msg := 'one or two'; ELSE msg := 'other value than one or two'; END CASE;
(三)循环表达式
1. 基础循环类型
-
LOOP 循环:支持
CONTINUE(跳过当前迭代)、EXIT(终止循环,等效于 break)sqlLOOP count := count + 1; CONTINUE WHEN count < 0; EXIT WHEN count > 100; END LOOP; -
WHILE 循环:
count := 0; WHILE count <= 100 LOOP count := count + 1; END LOOP; -
FOR 循环:
-
数值循环:
FOR i IN 1...100 LOOP END LOOP;(支持REVERSE反向、BY指定步长) -
结果集循环:自动创建游标,遍历查询结果
sqlDECLARE movie RECORD; FOR movie IN SELECT * FROM movies LOOP movie.id, movie.name... END LOOP;
-
2. 特殊循环语句
- EXECUTE 语句:动态构建 PL/pgSQL 命令字符串并执行,如
EXECUTE 'TRUNCATE TABLE' || table_name - PERFORM 语句:执行语句并忽略结果(如记录日志),如
PERFORM cs_log('Done refreshing materialized views');
3. 循环应用示例(斐波那契序列)
sql
CREATE OR REPLACE FUNCTION fib(n integer) RETURNS decimal(1000, 0) AS $$
DECLARE
counter integer := 0;
a decimal(1000, 0) := 0;
b decimal(1000, 0) := 1;
BEGIN
IF (n < 1) THEN RETURN 0; END IF;
LOOP
EXIT WHEN counter = n;
counter := counter + 1;
SELECT b, a+b INTO a, b;
END LOOP;
RETURN a;
END;
$$ LANGUAGE plpgsql;
(四)返回集合
1. 核心语法
- 声明方式:
RETURNS SETOF <类型>或TABLE(...),RETURNS SETOF RECORD - 数据返回:使用
RETURN NEXT(逐行返回)或RETURN QUERY(直接返回查询结果集)
2. 应用示例
-
斐波那契序列集合返回:
sqlCREATE OR REPLACE FUNCTION fib_seq(num integer) RETURNS SETOF integer AS $$ DECLARE a int := 0; b int := 1; BEGIN IF (num < 1) THEN RETURN; END IF; RETURN NEXT a; LOOP EXIT WHEN num <= 1; RETURN NEXT b; num := num - 1; SELECT b, a+b INTO a, b; END LOOP; END; $$ LANGUAGE plpgsql; -
查询系统已安装语言:
sqlCREATE OR REPLACE FUNCTION installed_languages() RETURNS SETOF pg_language AS $$ BEGIN RETURN QUERY SELECT * FROM pg_language; END; $$ LANGUAGE plpgsql;
3. 返回集合类型总结
| 返回类型声明 | RECORD 结构来源 | 函数内部实现方式 |
|---|---|---|
| SETOF <type> | 类型定义 | 声明 ROW/RECORD 变量,赋值后通过 RETURN NEXT 返回 |
| SETOF <table/view> | 对应表 / 视图结构 | 直接返回符合表 / 视图结构的结果集 |
| SETOF RECORD(动态) | 调用时通过 AS (名称 类型,...) 指定 | 动态构建结果集并返回 |
| SETOF RECORD(OUT 参数) | OUT/INOUT 函数参数 | 赋值给 OUT 变量,通过 RETURN NEXT 返回 |
| TABLE(...) | TABLE 关键字后括号内声明 | 转换为 OUT 变量,赋值后 RETURN NEXT 返回 |
(五)错误处理与异常
1. RAISE 语句
- 语法:
RAISE [level] 'format' [, expression [, ...]] [USING option = expression [, ...]]; - 级别(level):DEBUG、LOG、INFO、NOTICE、WARNING、EXCEPTION(默认级别为 EXCEPTION)
- 作用:
- EXCEPTION:抛出错误并终止当前事务
- NOTICE/INFO 等:输出提示信息(可在 pgAdmin 4 消息窗口查看)
- 示例:
RAISE NOTICE 'a = %, b = %, c = %', a, b, c;
2. 异常捕获
-
语法结构:
sqlBEGIN SELECT * INTO STRICT myrec FROM emp WHERE empname = myname; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE EXCEPTION 'employee % not found', myname; WHEN TOO_MANY_ROWS THEN RAISE EXCEPTION 'employee % not unique', myname; END; -
关键说明:
STRICT关键字要求查询必须返回恰好一行数据,否则触发异常
3. 异常信息获取
-
语法:
GET STACKED DIAGNOSTICS variable {=|:=} item [, ...] -
常用异常信息项:
名称 类型 描述 RETURNED_SQLSTATE text 异常的 SQLSTATE 错误码 MESSAGE_TEXT text 异常的主消息文本 TABLE_NAME text 与异常相关的表名 PG_EXCEPTION_DETAIL text 异常的详细消息文本(若有) PG_EXCEPTION_HINT text 异常的提示消息文本(若有)
4. FOUND 变量
- 作用:标识 SQL 语句执行结果状态
- 赋值规则:
- SELECT INTO:返回行则设为 true,否则 false
- PERFORM/UPDATE/INSERT/DELETE:影响至少一行则 true,否则 false
- FETCH:返回行则 true,否则 false
- FOR/FOREACH 循环:迭代至少一次则 true,否则 false
- RETURN QUERY:查询返回至少一行则 true,否则 false
(六)几何函数应用举例
1. 折线顶点提取(ST_PointsFromLine)
- 功能:提取折线(ST_LineString)的所有顶点,非线段类型返回空集
- 核心逻辑:通过
ST_NumPoints获取顶点数,循环使用ST_PointN提取顶点,最后用ST_Collect聚合为多点类型
2. 多边形内环数统计(ST_NInteriorRings)
- 功能:统计单个多边形或多多边形(ST_MultiPolygon)的内环总数
- 核心逻辑:多多边形时循环遍历每个子几何对象,累加内环数
3. 包围盒计算(ST_AABBEnvelope)
- 功能:计算几何对象的轴对齐包围盒(AABB)
- 核心逻辑:通过
ST_DumpPoints拆分所有顶点,获取ST_X/ST_Y的最大最小值,用ST_MakeEnvelope构建包围盒
4. 交叠关系判断(ST_GeomOverlaps)
- 交叠定义:两几何对象的内部维度相同,交集维度与内部维度相同,且交集不等于任一对象本身
- 核心逻辑:通过
ST_Intersection计算交集,验证维度和相等性条件