想象一下,你有个用来做复杂计算或者数据处理的函数。如果不能把函数集成进SQL查询,你得这么干:
- 从某个编程语言(比如Python或者JavaScript)里调用函数。
- 把结果传给SQL查询。
这多此一举!在PostgreSQL里,函数可以直接嵌进SQL查询里,这样代码更少,操作更快,和服务器的交互也少了。这在这些场景下特别爽:
- 自动化计算。
- 插入前校验数据。
- 修改已有数据。
在SELECT里调用函数
先从基础讲起,看看怎么在普通SELECT查询里用函数。假设我们有个叫students的表,存着学生的信息。我们想写个函数,能根据出生日期返回学生当前年龄。
第1步:写函数
来写个calculate_age函数,接收出生日期,返回年龄:
CREATE OR REPLACE FUNCTION calculate_age(birth_date DATE) RETURNS INT AS $$
BEGIN
RETURN DATE_PART('year', AGE(NOW(), birth_date))::INT;
END;
$$ LANGUAGE plpgsql;
第2步:在SELECT查询里用函数
现在我们可以对表里的每条记录调用这个函数:
SELECT id, name, calculate_age(birth_date) AS age FROM students;
发生了什么?
- 对
students表的每一行,calculate_age函数都会算出年龄。 - 返回值会显示在
age这一列。
结果示例:
| id | name | age |
|---|---|---|
| 1 | 奥托 | 21 |
| 2 | 安娜 | 25 |
| 3 | 阿列克斯 | 22 |
你看,没啥难的,结果还挺专业挺好看。
在INSERT里调用函数
插入数据时函数也很有用。比如我们有个logs表,记录用户的操作。我们想插入日志消息时,用函数自动生成文本。
第1步:写函数
写个generate_log_message函数,接收用户名和动作,返回消息文本:
CREATE OR REPLACE FUNCTION generate_log_message(username TEXT, action TEXT) RETURNS TEXT AS $$
BEGIN
RETURN username || ' performed action: ' || action || ' at ' || NOW();
END;
$$ LANGUAGE plpgsql;
第2步:在INSERT里用函数
现在插入logs表时,直接用函数生成消息:
INSERT INTO logs (message)
VALUES (generate_log_message('奥托', '登录网站'));
结果:
| id | message |
|---|---|
| 1 | 奥托 performed action: 登录网站 at 2023-10-26 12:00:00 |
函数帮我们搞定了文本格式和时间戳。这就是函数自动化日常操作的好例子。
在UPDATE里调用函数
函数还能用来修改表里的数据。比如我们有students表,想用函数把他们的组名升级到新年级。
第1步:写函数
写个promote_student函数,接收旧组名(比如101),返回新组名(比如201):
CREATE OR REPLACE FUNCTION promote_student(old_group TEXT) RETURNS TEXT AS $$
BEGIN
RETURN '2' || RIGHT(old_group, LENGTH(old_group) - 1);
END;
$$ LANGUAGE plpgsql;
第2步:在UPDATE里用函数
把所有学生的组都更新一下:
UPDATE students
SET group_name = promote_student(group_name);
结果:
| id | name | group_name |
|---|---|---|
| 1 | 奥托 | 201 |
| 2 | 安娜 | 202 |
| 3 | 阿列克斯 | 203 |
你看,函数一调用,组名就自动升级了,简直像变魔术。
在WHERE条件里调用函数
函数还能用在过滤条件里。我们继续用学生年龄的例子。
第1步:按年龄过滤
用刚才写的calculate_age函数,选出大于20岁的学生:
SELECT id, name, birth_date
FROM students
WHERE calculate_age(birth_date) > 20;
结果:
| id | name | birth_date |
|---|---|---|
| 2 | 安娜 | 1998-05-15 |
| 3 | 阿列克斯 | 1999-11-09 |
这里主要就是函数帮我们实时算出每个学生的年龄。
和聚合函数一起用
来点难度。比如我们要算22岁以下学生的总数。函数和聚合函数(比如COUNT())配合得很溜。
SELECT COUNT(*)
FROM students
WHERE calculate_age(birth_date) < 22;
发生了什么?
calculate_age函数用来过滤。COUNT(*)统计符合条件的行数。
实际应用例子
自动化数据校验。 比如你想检查表里所有学生的年龄都在正常范围(比如18到30岁)。写个校验函数,然后在WHERE条件里用它。
SELECT id, name
FROM students
WHERE NOT (calculate_age(birth_date) BETWEEN 18 AND 30);
优化数据插入。 假设你在做电商。与其在前端算订单总价,不如写个函数,直接在插入orders表时算好。
INSERT INTO orders (user_id, total_price)
VALUES (1, calculate_total_price(ARRAY[5, 10, 15]));
调用函数时常见的坑
你开始在查询里大量用函数时,可能会遇到一些坑。下面是常见的几种情况和解决办法:
没有权限。 如果你不是函数或表的owner,PostgreSQL可能不让你用。记得要有执行函数的权限。
类型不匹配。 传参数给函数时注意数据类型。比如函数要DATE,你传了字符串,就会报错。用显式类型转换:
SELECT calculate_age('2000-01-01'::DATE);
函数内部语法错。 如果函数报错,整个查询都可能挂。用之前一定要好好测试函数。
GO TO FULL VERSION