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;
相关文章
|
21天前
|
SQL 存储 关系型数据库
PL/pgSQL 入门教程(一):语法篇
本教程为PL/pgSQL入门首篇,系统讲解其核心基础与语法规则。涵盖函数创建、块结构、变量声明、参数传递、返回类型及排序规则等关键知识点,助你掌握在PostgreSQL中编写高效存储过程与函数的必备技能,提升数据库逻辑处理能力。
|
1月前
|
存储 缓存 调度
阿里云Tair KVCache仿真分析:高精度的计算和缓存模拟设计与实现
在大模型推理迈向“智能体时代”的今天,KVCache 已从性能优化手段升级为系统级基础设施,“显存内缓存”模式在长上下文、多轮交互等场景下难以为继,而“以存代算”的多级 KVCache 架构虽突破了容量瓶颈,却引入了一个由模型结构、硬件平台、推理引擎与缓存策略等因素交织而成的高维配置空间。如何在满足 SLO(如延迟、吞吐等服务等级目标)的前提下,找到“时延–吞吐–成本”的最优平衡点,成为规模化部署的核心挑战。
508 38
阿里云Tair KVCache仿真分析:高精度的计算和缓存模拟设计与实现
|
5天前
|
机器学习/深度学习
机器学习特征工程:分类变量的数值化处理方法
分类特征编码是机器学习关键却常被低估的环节。Ordinal Encoding适用于有序类别(如学历),One-Hot Encoding消除顺序假象但易致维度爆炸,Target Encoding则通过目标均值处理高基数特征,需配合平滑与交叉验证防过拟合与数据泄露。
61 5
|
15天前
|
数据库
向量数据库实战:从“看起来能用”到“真的能用”,中间隔着一堆坑
本文揭示向量数据库实战的七大关键陷阱:选型前需明确业务本质(模糊匹配 or 精确查询?);embedding 比数据库本身更重要,决定语义“世界观”;文档切分是核心工程,非辅助步骤;建库成功≠可用,TopK 准确率会随数据演进失效;“相似但不可用”是常态,必须引入 rerank;需建立可追溯的bad case排查路径;向量库是长期系统,非一次性组件。核心结论:难在“用对”,不在“用上”。
|
1月前
|
负载均衡 容灾 JavaScript
Nginx反向代理容灾备份(手把手教你搭建高可用Web服务)
本文介绍如何通过Nginx反向代理实现容灾备份与高可用架构。利用upstream模块配置主备服务器,结合健康检查与自动故障转移,确保主服务宕机时无缝切换至备用服务器。图文详解参数设置、配置步骤及测试方法,并提供Keepalived、HTTPS等进阶优化建议,助小白快速搭建稳定可靠的Web系统。
|
13天前
|
人工智能 关系型数据库 Serverless
2 天,用函数计算 AgentRun 爆改一副赛博朋克眼镜
2 天将吃灰的 Meta 眼镜改造成“交警Copilot”:通过阿里云函数计算 AgentRun 实现端-管-云协同,利用 Prompt 驱动交通规则判断,结合 OCR 与数据库查询,打造可动态扩展的智能执法原型,展现 Agent 架构在真实场景中的灵活与高效。
297 44
|
14天前
|
机器学习/深度学习 人工智能 自然语言处理
模型训练篇|多阶段ToolRL打造更可靠的AI导购助手
芝麻租赁推出AI导购“租赁小不懂”,针对长周期、重决策租赁场景,首创“One-Model + Tool-Use”架构与两阶段强化学习,攻克需求难匹配、决策效率低、服务被动三大痛点,实现响应提速78%、推荐成功率提升14.93%,打造贴切、沉浸、信任的场景化租赁体验。(239字)
157 25
模型训练篇|多阶段ToolRL打造更可靠的AI导购助手
|
17天前
|
人工智能 自然语言处理 物联网
Qwen-Image 从推理到 LoRA 训练实战教程(AMD GPU × DiffSynth-Studio)
本课程由魔搭社区出品,详解如何在AMD GPU上基于DiffSynth-Studio框架高效部署、微调与训练Qwen-Image系列大模型(860亿参数)。涵盖文生图推理、LoRA画质增强、多语言提示理解、高一致性人像外延及多图融合编辑,并支持从零训练专属LoRA(如定制狗狗生成)。
492 40
|
30天前
|
数据采集 监控 数据可视化
快速上手:LangChain + AgentRun 浏览器沙箱极简集成指南
AgentRun Browser Sandbox 是基于云原生函数计算的浏览器沙箱服务,为 AI Agent 提供安全、免运维的浏览器环境。通过 Serverless 架构与 CDP 协议支持,实现网页抓取、自动化操作等能力,并结合 VNC 实时可视化,助力大模型“上网”交互。
494 43
|
6天前
|
存储 数据采集 安全
微调与安全隐私 —— 大模型落地的合规必修课
本文聚焦大模型微调中的安全与隐私合规风险,系统剖析数据集、训练、输出三大环节的典型隐患(如敏感信息泄露、版权侵权、模型反演等),提出“脱敏+加密+过滤”全流程防护方案,并推荐合规认证平台与实操工具,助力企业实现技术落地与法律合规双保障。(239字)