PL/pgSQL 入门教程(二):表达式和基础语句

简介: 本文详解PL/pgSQL核心语法:表达式由主SQL引擎以参数化SELECT执行,支持计划缓存;基础语句涵盖赋值(:=/=)、静态/动态SQL执行(INTO/PERFORM/EXECUTE)、结果处理(STRICT模式)、状态获取(FOUND/GET DIAGNOSTICS)及空操作NULL。

一、表达式

PL/pgSQL 语句所使用的所有表达式,均由服务器的主 SQL 执行器统一处理。

1. 表达式执行原理

  1. 转换为 SELECT 命令

当执行包含表达式的 PL/pgSQL 语句(如 IF expression THEN ...)时,解释器会将该表达式转换为 SELECT expression 语句,提交至主 SQL 引擎执行计算。

例如:

IF x < y THEN ...

底层等价于执行:

PREPARE statement_name(integer, integer) AS SELECT $1 < $2;
EXECUTE statement_name(x, y);
  1. 变量参数化与计划缓存

    • 表达式中的 PL/pgSQL 变量会被替换为查询参数$1$2...),而非直接拼接变量值,从源头规避语法冲突风险;

    • SELECT 语句的执行计划仅需预编译一次,后续调用可复用此计划并传入不同变量值,有效提升执行性能。

  2. 语法限制

表达式对应的 SELECT 命令支持普通 SELECT 的大部分子句,但 包含顶层 UNION INTERSECT EXCEPT 子句,否则将触发语法错误。

  1. 结果要求

表达式对应的 SELECT 语句必须返回单个列,且返回行数不超过 1 行,具体规则如下:

2. 典型示例

判断表是否非空:

IF count(*) > 0 FROM my_table THEN ...

该表达式等价于执行 SELECT count(*) > 0 FROM my_table,系统将根据查询返回的布尔值执行对应分支逻辑。

二、基础语句

2.1 赋值操作

1. 语法格式

variable { := | = } expression;
  • 赋值符:兼容 PL/SQL 风格的 := 与 SQL 风格的 =,两种格式均可正常使用;

  • 目标变量范围:可包括普通变量、行/记录类型的字段、数组元素及数组切片。

2. 表达式求值规则

  • 赋值语句中的表达式,同样会转换为 SELECT 命令执行;

  • 表达式必须返回单个值(若变量是行/记录类型,可返回行值)。

3. 类型转换规则

若表达式结果类型与变量类型不匹配,系统将按以下优先级执行类型转换:

  1. 赋值类型转换:使用预设的类型转换规则;

  2. 文本转换:若无预设规则,先将结果转为字符串(调用类型的输出函数),再转为目标类型(调用目标类型的输入函数),可能触发运行时错误。

4. 示例

tax := subtotal * 0.06; -- 普通变量赋值
my_record.user_id := 20; -- 记录字段赋值
my_array[1:3] := array[1,2,3]; -- 数组切片赋值
complex_array[n].realpart = 12.3; -- 嵌套数组字段赋值

2.2 执行 SQL 命令

PL/pgSQL 中执行 SQL 命令分为静态命令动态命令两类,二者核心差异在于是否支持变量参数化及执行计划缓存机制。

1. 执行无返回行的 SQL 命令

对于无返回行的 SQL 命令,可直接编写执行,例如 CREATE TABLE、无 RETURNING 子句的INSERTUPDATE 等语句,示例如下:


CREATE TABLE mytable (id int primary key, data text);
INSERT INTO mytable VALUES (1,'one'), (2,'two');

2. 执行有返回行的 SQL 命令

针对有返回行的 SQL 命令,需根据返回行数采用对应的处理方式,具体如下表所示:

场景 处理方式
最多返回 1 行 添加 INTO 子句,将结果赋值给变量
返回多行 作为 FOR 循环的数据源,遍历结果

3. 静态 SQL 命令(可优化命令)

适用场景SELECT 语句、带 RETURNING子句的 INSERT/UPDATE/DELETE/MERGE 语句及 EXPLAIN 语句等。

  • 变量参数化:命令中的 PL/pgSQL 变量会自动替换为查询参数,有效防范 SQL 注入攻击;

  • 计划缓存:执行计划会被系统缓存并复用,显著提升语句重复执行时的效率。

4. 丢弃结果的执行:PERFORM 语句

当需执行查询但无需使用返回结果时(如调用具有副作用的函数),需使用 PERFORM 语句替代 SELECT 语句,语法格式如下:

PERFORM query;
  • 语法规范:将常规 SELECT 替换为 PERFORM;若查询包含 WITH 子句,需用括号包裹整个查询语句;

  • 状态反馈:执行完成后,系统会更新特殊变量 FOUND,若查询返回至少 1 行结果则设为 true,无返回结果则设为 false

注意事项:直接使用 SELECT 语句且不搭配 INTO 子句时,将触发语法错误,需优先使用 PERFORM 语句。

5. 非优化命令(工具类命令)

适用 场景CREATE INDEXALTER TABLE 等工具类命令,此类命令不支持变量参数化。

  • 无法自动替换变量,必须通过动态命令拼接字符串执行。

2.3 执行返回单行结果的命令

通过 INTO 子句,可将单行查询结果赋值给记录变量行变量标量变量列表,实现结果的精准接收与后续处理。

1. 语法格式

SELECT select_expressions INTO [STRICT] target FROM ...;
INSERT ... RETURNING expressions INTO [STRICT] target;
UPDATE ... RETURNING expressions INTO [STRICT] target;
DELETE ... RETURNING expressions INTO [STRICT] target;
MERGE ... RETURNING expressions INTO [STRICT] target;

2. 关键参数说明

参数 说明
target 接收结果的变量:单个记录/行变量,或逗号分隔的标量变量列表
STRICT 严格模式:要求命令必须返回且仅返回 1 行结果,若不符合条件则抛出对应异常。

3. 执行规则

  1. 非严格模式(默认)

    • 返回 1 行时,将结果赋值给 target 指定的变量;

    • 返回 0 行时,target 变量值被设为 NULL

    • 返回多行时,仅提取第一行结果赋值,其余行结果将被丢弃;

    • 可通过 FOUND 变量判断是否成功返回数据。

  2. 严格模式(STRICT)

    • 返回 0 行时,抛出 NO_DATA_FOUND 异常;

    • 返回多行时,抛出 TOO_MANY_ROWS 异常;

    • 执行成功时,FOUND 变量自动设为 true

特殊限制:对于带 RETURNING 子句的 INSERT/UPDATE 等命令,即使未指定 STRICT 模式,返回多行结果也会抛出异常。

4. 语法注意事项

  • INTO 子句位置:SELECT 命令中建议置于查询字段前后,其他类型命令建议置于语句末尾,保证语法规范性;

  • 与原生 SELECT INTO 的区别:PL/pgSQL 中 INTO 用于结果赋值,原生命令中 INTO 用于创建表;若需创建表,应使用 CREATE TABLE ... AS SELECT 语句。

5. 调试优化:print_strict_params

启用该选项后,严格模式下触发异常时,会在 DETAIL 信息中显示具体参数值,为问题排查提供便利:

CREATE FUNCTION get_userid(username text) RETURNS int
AS $$
#print_strict_params on
DECLARE userid int;
BEGIN
    SELECT users.userid INTO STRICT userid FROM users WHERE users.username = username;
    RETURN userid;
END;
$$ LANGUAGE plpgsql;

2.4 执行动态命令

当 SQL 命令需动态指定表名、字段名等标识符时,需使用 EXECUTE 语句执行动态拼接的 SQL 字符串,实现灵活适配。

1. 语法格式

EXECUTE command-string [ INTO [STRICT] target ] [ USING expression [, ... ] ];
参数 说明
command-string 动态拼接的 SQL 命令字符串(text 类型)
target 接收结果的变量
USING 传入命令的参数,对应字符串中的 $1$2...

2. 核心特性

  1. 无自动变量替换:需手动将变量拼接至字符串,或通过 USING 子句传入参数,不可依赖系统自动替换;

  2. 无执行计划缓存:每次执行都会重新生成执行计划,适用于动态表/字段等非固定结构的操作场景;

  3. 参数化传值(推荐):通过 USING 子句传入参数,既能避免 SQL 注入风险,又无需手动转义特殊字符,提升安全性与效率。

3. 关键用法示例

场景 示例代码
带参数的动态查询 EXECUTE 'SELECT count(*) FROM mytable WHERE inserted_by = $1' INTO c USING checked_user;
动态表名(安全转义) EXECUTE format('SELECT count(*) FROM %I', tabname) INTO c;
动态字段赋值 EXECUTE format('UPDATE tbl SET %I = $1', colname) USING newvalue;

4. 安全转义函数

拼接动态表名、字段名或变量值时,必须使用指定转义函数,以规避语法错误及 SQL 注入风险,保障命令安全执行:

函数 用途 示例
quote_ident 转义标识符(表名、字段名) quote_ident('my table') → "my table"
quote_literal 转义字符串值(NULL 会返回 NULL quote_literal('a''b') → 'a''b'
quote_nullable 转义值(NULL 会返回字符串 NULL quote_nullable(NULL) → NULL

推荐方案:使用 format 函数的 %I(标识符占位符)和 %L(值占位符),简化转义操作,提升代码可读性。

2.5 获取执行状态

PL/pgSQL 提供两种方式获取命令执行后的状态信息,分别适用于详细指标查询与快速结果判断场景。

1. GET DIAGNOSTICS 命令

用于获取详细的系统状态指标,语法格式如下:

GET [ CURRENT ] DIAGNOSTICS variable { = | := } item [ , ... ];

支持的状态项:

状态项 类型 描述
ROW_COUNT bigint 最近一次 SQL 命令处理的行数
PG_CONTEXT text 当前调用栈信息
PG_ROUTINE_OID oid 当前函数的 OID

示例:

GET DIAGNOSTICS cnt = ROW_COUNT; -- 获取受影响行数

2. FOUND 变量

FOUND 为特殊布尔变量,用于快速判断命令是否产生有效结果。该变量在每次函数调用时初始化为 false,并由以下语句更新状态:

语句类型 FOUND 设为 true 的条件
SELECT INTO 成功返回 1 行
PERFORM 返回至少 1 行
INSERT/UPDATE/DELETE/MERGE 至少影响 1 行
FETCH/MOVE 成功获取/移动游标
FOR/FOREACH 循环至少执行 1 次
RETURN QUERY 查询返回至少 1 行

注意 事项EXECUTE 语句会更新 ROW_COUNT 变量值,但不会修改 FOUND 变量的状态,需单独通过其他方式判断结果。

2.6 空操作语句:NULL

空操作语句(NULL;)用于逻辑占位,无任何实际执行效果,语法格式如下:

NULL;

典型用途:标记 IF/EXCEPTION 等分支的空逻辑,明确代码意图,提升代码可读性与可维护性。

示例:

BEGIN
    y := x / 0;
EXCEPTION
    WHEN division_by_zero THEN
        NULL; -- 明确表示忽略错误
END;
相关文章
|
3月前
|
SQL 存储 关系型数据库
PL/pgSQL 入门教程(一):语法篇
本教程为PL/pgSQL入门首篇,系统讲解其核心基础与语法规则。涵盖函数创建、块结构、变量声明、参数传递、返回类型及排序规则等关键知识点,助你掌握在PostgreSQL中编写高效存储过程与函数的必备技能,提升数据库逻辑处理能力。
|
关系型数据库 MySQL Linux
TiDB实时同步数据到PostgreSQL(三) ---- 使用pgloader迁移数据
使用PostgreSQL数据迁移神器pgloader从TiDB迁移数据到PostgreSQL,同时说明如何在最新的Rocky Linux 9(CentOS 9 stream也适用)上通过源码编译安装pgloader。
|
2月前
|
SQL 关系型数据库 数据挖掘
PostgreSQL窗口函数从入门到实操
PostgreSQL窗口函数是数据分析利器:统计不丢行、细节全保留!本文用大白话讲透核心概念(分区/排序/窗口帧),配可运行示例,带你零基础掌握排名、移动平均、累积求和等高频场景,学完即用。
|
3月前
|
算法 数据挖掘 API
淘宝店铺全量商品API接口技术实践指南
本文详解淘宝开放平台taobao.item_search_shop接口,涵盖核心功能、参数配置、签名生成、调用流程及实战要点,助力开发者高效获取店铺全量商品数据,实现电商运营与数据分析的自动化对接。(238字)
|
3月前
|
数据库 C++ Perl
PL/pgSQL 入门教程(三):控制结构
本文详解PL/pgSQL核心编程:函数返回(RETURN单值、RETURN NEXT/QUERY多行)、条件判断(IF/CASE)、循环控制(LOOP/WHILE/FOR/FOREACH)及异常处理(EXCEPTION),附丰富示例与最佳实践,助你写出健壮高效的数据库逻辑。
|
3月前
|
SQL 存储 缓存
PL/pgSQL 入门教程(六):从避坑到吃透,聊聊事务、错误处理和底层那些事儿
本文深度解析PL/pgSQL开发避坑指南:详解RAISE多级错误处理与USING增强提示、EXCEPTION事务恢复机制、变量替换限制与计划缓存陷阱,并分享美元符引号、CREATE OR REPLACE调试、extra_warnings预警等实战技巧,助你写出健壮高效存储过程。
|
3月前
|
SQL 监控 关系型数据库
PL/pgSQL 入门教程(五):触发器
PostgreSQL触发器是数据库的“自动服务员”,可在INSERT/UPDATE/DELETE等操作时自动执行校验、日志记录、汇总更新等逻辑。支持BEFORE/AFTER/INSTEAD OF时机,ROW/STATEMENT级别,配合NEW/OLD变量实现灵活数据管控,大幅提升数据一致性与运维效率。
|
3月前
|
搜索推荐 关系型数据库 大数据
PL/pgSQL 入门教程(四):使用游标(cursor)
游标是PostgreSQL中“按需取数”的数据指针,避免大查询内存溢出;支持逐行处理、动态查询、精准更新/删除及函数返回大结果集。分未绑定(灵活)与绑定(固定)两类,核心操作为声明→打开→FETCH/MOVE/UPDATE→关闭,FOR循环可自动简化遍历。
|
3月前
|
SQL 关系型数据库 数据库
用C语言开发PostgreSQL用户自定义函数之数据查询篇
本教程教你用C语言编写PostgreSQL的UDF函数,通过SPI接口执行SQL查询,利用SRF机制返回多行数据。涵盖头文件引入、函数编写、编译部署及SQL调用全流程,并附内存管理与列序号等避坑提示,助你掌握C语言扩展PostgreSQL的核心技术。
|
3月前
|
SQL 存储 关系型数据库
PostgreSQL SQL函数语法详解
本文深入讲解PostgreSQL中SQL语言函数的编写,涵盖参数引用、返回类型(基类型/复合类型/集合)、输出参数、可变参数、默认值、多态函数及排序规则等核心特性,系统阐述其语法、行为与最佳实践。

热门文章

最新文章

下一篇
开通oss服务