PostgreSQL: PostgreSQL内置函数详解

最后更新:2026-08-26

1. 你将学到


2. 故事

Alice 是电商数据分析师,月底需要生成月度销售报表。报表需求包括:

  1. 日期格式化:将 order_date 显示为 2025-June-30 格式
  2. 字符串拼接:客户名 + 订单号生成报表行标题
  3. NULL 处理:缺失的折扣值默认为 0
  4. 金额计算:含税总价、折扣后净额的精确计算
  5. 序列使用:为报表行生成连续编号

Alice 需要组合使用 PostgreSQL 内置函数来完成报表。


3. Concept:数学函数

(1) 基础数学函数

函数 含义 示例 结果
ABS(x) 绝对值 ABS(-15) 15
ROUND(x, n) 四舍五入 ROUND(3.1415, 2) 3.14
CEIL(x) 向上取整 CEIL(3.2) 4
FLOOR(x) 向下取整 FLOOR(3.8) 3
MOD(x, y) 取模 MOD(10, 3) 1
POWER(x, y) POWER(2, 10) 1024
SQRT(x) 平方根 SQRT(144) 12
SIGN(x) 符号 SIGN(-5) -1
TRUNC(x, n) 截断 TRUNC(3.1415, 2) 3.14

▶ 示例:ROUND 四舍五入

SQL
SELECT
  unit_price,
  ROUND(unit_price, 0) AS rounded_0,
  ROUND(unit_price, 2) AS rounded_2
FROM products
WHERE category = 'Electronics';

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ 示例:CEIL / FLOOR 取整

SQL
SELECT
  total_amount,
  CEIL(total_amount) AS ceil_value,
  FLOOR(total_amount) AS floor_value,
  total_amount - FLOOR(total_amount) AS decimal_part
FROM orders
WHERE order_status = 'completed';

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) ROUND vs TRUNC vs CEIL/FLOOR

函数 行为 3.5 结果 3.2 结果 -3.5 结果
ROUND(x) 四舍五入到整数 4 3 -4
TRUNC(x) 截断小数 3 3 -3
CEIL(x) 向上取整 4 4 -3
FLOOR(x) 向下取整 3 3 -4

▶ 示例:MOD 取模运算

SQL
SELECT
  order_id,
  total_amount,
  MOD(order_id, 10) AS partition_key,
  total_amount * POWER(1.08, 1) AS with_annual_tax
FROM orders
WHERE order_status = 'completed';

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ 示例:SQRT 与 POWER 组合

SQL
SELECT
  unit_price,
  SQRT(unit_price) AS price_sqrt,
  POWER(unit_price, 1.0 / 3) AS price_cbrt
FROM products
WHERE unit_price > 0
ORDER BY unit_price DESC;

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

4. Concept:字符串函数

(1) 大小写与长度

函数 含义 示例 结果
LOWER(s) 转小写 LOWER('Hello') hello
UPPER(s) 转大写 UPPER('Hello') HELLO
INITCAP(s) 首字母大写 INITCAP('hello world') Hello World
LENGTH(s) 字符数 LENGTH('PostgreSQL') 10
BIT_LENGTH(s) 位长度 BIT_LENGTH('A') 8
CHAR_LENGTH(s) 同 LENGTH CHAR_LENGTH('ABC') 3

▶ 示例:大小写转换

SQL
SELECT
  product_name,
  LOWER(product_name) AS lower_name,
  UPPER(product_name) AS upper_name,
  INITCAP(LOWER(product_name)) AS title_name
FROM products
WHERE is_active = true;

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) 连接与截取

函数 含义 示例 结果
CONCAT(s1, s2, ...) 连接(NULL 安全) CONCAT('A', NULL, 'B') AB
CONCAT_WS(sep, s1, s2) 带分隔符连接 CONCAT_WS('-', 'A','B') A-B
`s1 s2` 连接(NULL 不安全)
SUBSTRING(s FROM n FOR len) 截取 SUBSTRING('Hello' FROM 2 FOR 3) ell
LEFT(s, n) 左取 n 字符 LEFT('Hello', 3) Hel
RIGHT(s, n) 右取 n 字符 RIGHT('Hello', 2) lo

▶ 示例:CONCAT vs ||

SQL
SELECT
  product_name,
  CONCAT(product_name, ' - $', unit_price) AS safe_concat,
  product_name || ' - $' || unit_price AS unsafe_concat,
  CONCAT_WS(' | ', product_name, category, unit_price::TEXT) AS ws_concat
FROM products;

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(3) 查找与替换

函数 含义 示例 结果
POSITION(sub IN s) 查找位置 POSITION('sql' IN 'postgresql') 8
STRPOS(s, sub) 同上(参数反) STRPOS('postgresql', 'sql') 8
REPLACE(s, from, to) 替换 REPLACE('ABC', 'B', 'X') AXC
OVERLAY(s PLACING t FROM n) 覆盖 OVERLAY('XXXX' PLACING 'AB' FROM 2) XABX
SPLIT_PART(s, delim, n) 按分隔符取第 n 段 SPLIT_PART('A-B-C', '-', 2) B
TRIM(s) 去两端空格 TRIM(' Hi ') Hi
LTRIM(s) 去左空格 LTRIM(' Hi ') Hi
RTRIM(s) 去右空格 RTRIM(' Hi ') Hi
RPAD(s, len, fill) 右填充 RPAD('Hi', 5, 'x') Hixxx
LPAD(s, len, fill) 左填充 LPAD('42', 5, '0') 00042
REVERSE(s) 反转 REVERSE('ABC') CBA

▶ 示例:SPLIT_PART 解析 CSV 字段

SQL
SELECT
  product_name,
  SPLIT_PART(category_path, '/', 1) AS top_category,
  SPLIT_PART(category_path, '/', 2) AS sub_category
FROM products
WHERE category_path LIKE '%/%';

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ 示例:REPLACE 与 TRIM 组合

SQL
SELECT
  TRIM(product_name) AS clean_name,
  REPLACE(product_name, '  ', ' ') AS no_double_spaces
FROM products
WHERE is_active = true;

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ 示例:LPAD 格式化编号

SQL
SELECT
  LPAD(product_id::TEXT, 8, '0') AS formatted_id,
  product_name,
  unit_price
FROM products
ORDER BY product_id;

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

5. Concept:日期函数

(1) 获取当前时间

函数 返回类型 说明
CURRENT_DATE DATE 当前日期
CURRENT_TIME TIME WITH TZ 当前时间(含时区)
CURRENT_TIMESTAMP TIMESTAMP WITH TZ 当前日期和时间
NOW() TIMESTAMP WITH TZ 同 CURRENT_TIMESTAMP
LOCALTIMESTAMP TIMESTAMP 当前时间(不含时区)
CLOCK_TIMESTAMP() TIMESTAMP WITH TZ 实时时钟(每次调用不同)

▶ 示例:获取当前时间

SQL
SELECT
  CURRENT_DATE AS today,
  CURRENT_TIMESTAMP AS now_ts,
  NOW() AS now_func,
  LOCALTIMESTAMP AS local_ts;

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) 日期运算

表达式 含义 结果示例
DATE '2025-01-15' + 7 日期加天数 2025-01-22
DATE '2025-01-15' - DATE '2025-01-01' 日期差(天数) 14
NOW() + INTERVAL '30 days' 时间加间隔 30 天后
NOW() - INTERVAL '1 year' 时间减间隔 1 年前
AGE(end, start) 间隔描述 1 year 2 mons 3 days
AGE(timestamp) 距当前时间间隔

▶ 示例:日期运算

SQL
SELECT
  order_date,
  order_date + 7 AS due_date,
  CURRENT_DATE - order_date AS days_ago,
  AGE(CURRENT_DATE, order_date) AS age_since_order
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '90 days';

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(3) EXTRACT 与 DATE_TRUNC

用法 含义 示例 结果
EXTRACT(YEAR FROM ts) 提取年 EXTRACT(YEAR FROM NOW()) 2025
EXTRACT(MONTH FROM ts) 提取月 EXTRACT(MONTH FROM NOW()) 6
EXTRACT(DAY FROM ts) 提取日
EXTRACT(DOW FROM ts) 星期几(0=周日) 1(周一)
EXTRACT(QUARTER FROM ts) 季度 2
EXTRACT(EPOCH FROM ts) Unix 时间戳 1719500400
DATE_TRUNC('month', ts) 截断到月初 DATE_TRUNC('month', '2025-06-15') 2025-06-01
DATE_TRUNC('year', ts) 截断到年初 2025-01-01

▶ 示例:EXTRACT 提取日期部分

SQL
SELECT
  order_date,
  EXTRACT(YEAR FROM order_date) AS order_year,
  EXTRACT(MONTH FROM order_date) AS order_month,
  EXTRACT(QUARTER FROM order_date) AS order_quarter,
  EXTRACT(DOW FROM order_date) AS weekday
FROM orders
WHERE order_status = 'completed';

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ 示例:DATE_TRUNC 按月汇总

SQL
SELECT
  DATE_TRUNC('month', order_date) AS month_start,
  COUNT(*) AS order_count,
  SUM(total_amount) AS monthly_revenue
FROM orders
WHERE order_status = 'completed'
  AND order_date >= DATE_TRUNC('year', CURRENT_DATE)
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY month_start;

输出:

TEXT 📖 仅展示
 count 
-------
     5
(1 row)

(4) TO_CHAR 与 TO_DATE

函数 用途 示例
TO_CHAR(ts, fmt) 时间→字符串 TO_CHAR(NOW(), 'YYYY-Month-DD')
TO_DATE(s, fmt) 字符串→日期 TO_DATE('2025-06-15', 'YYYY-MM-DD')
TO_TIMESTAMP(s, fmt) 字符串→时间戳 TO_TIMESTAMP('2025-06-15 14:30', 'YYYY-MM-DD HH24:MI')

常用格式模板:

模板 含义 示例输出
YYYY 四位年 2025
YY 两位年 25
Month 月份全名 June
MON 月份缩写 Jun
MM 两位月 06
DD 两位日 15
HH24 24小时 14
MI 分钟 30
SS 45

▶ 示例:TO_CHAR 格式化日期

SQL
SELECT
  order_id,
  TO_CHAR(order_date, 'YYYY-Month-DD') AS formatted_date,
  TO_CHAR(order_date, 'YYYY-MM-DD HH24:MI:SS') AS full_datetime,
  TO_CHAR(total_amount, 'FM$999,999.00') AS formatted_amount
FROM orders
WHERE order_status = 'completed';

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ 示例:TO_DATE 字符串转日期

SQL
SELECT
  TO_DATE('2025-06-15', 'YYYY-MM-DD') AS parsed_date,
  TO_DATE('15/06/2025', 'DD/MM/YYYY') AS eu_date,
  TO_DATE('Jun 15 2025', 'Mon DD YYYY') AS us_date;

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

6. Concept:条件函数

(1) COALESCE

COALESCE(val1, val2, ...) 返回第一个非 NULL 的参数,是最常用的 NULL 处理函数。

表达式 结果
COALESCE(NULL, NULL, 'default') default
COALESCE(100, 0) 100
COALESCE(NULL, 0) 0

▶ 示例:COALESCE 处理缺失折扣

SQL
SELECT
  product_name,
  unit_price,
  discount_rate,
  COALESCE(discount_rate, 0) AS safe_discount,
  unit_price * (1 - COALESCE(discount_rate, 0)) AS net_price
FROM products;

输出:

TEXT 📖 仅展示
 count 
-------
     5
(1 row)

(2) NULLIF

NULLIF(val1, val2) 当两值相等时返回 NULL,否则返回 val1。常用于避免除零错误。

表达式 结果
NULLIF(100, 100) NULL
NULLIF(100, 0) 100
100 / NULLIF(0, 0) NULL(而非报错)

▶ 示例:NULLIF 防止除零

SQL
SELECT
  product_name,
  unit_price,
  total_sold,
  unit_price / NULLIF(total_sold, 0) AS revenue_per_unit
FROM products
WHERE is_active = true;

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(3) GREATEST / LEAST

函数 含义 示例 结果
GREATEST(a, b, c) 返回最大值 GREATEST(10, 20, 5) 20
LEAST(a, b, c) 返回最小值 LEAST(10, 20, 5) 5

▶ 示例:GREATEST / LEAST

SQL
SELECT
  product_name,
  unit_price,
  COALESCE(discount_rate, 0) AS discount,
  unit_price * (1 - GREATEST(COALESCE(discount_rate, 0), 0.5)) AS capped_net_price,
  LEAST(unit_price, 999.99) AS price_cap
FROM products
WHERE is_active = true;

输出:

TEXT 📖 仅展示
 count 
-------
     5
(1 row)

(4) 条件函数对比

函数 场景 NULL 处理
COALESCE(a, b) 提供默认值 返回第一个非 NULL
NULLIF(a, b) 值相等时变 NULL 不处理 NULL
GREATEST(a, b) 取最大值 任一 NULL 则返回 NULL
LEAST(a, b) 取最小值 任一 NULL 则返回 NULL

7. Concept:序列函数

(1) 序列操作函数

函数 含义 示例
NEXTVAL('seq_name') 递增并返回下一个值 NEXTVAL('order_id_seq') → 1001
CURRVAL('seq_name') 返回当前会话最近一次 NEXTVAL 的值 CURRVAL('order_id_seq') → 1001
SETVAL('seq_name', n) 设置序列当前值 SETVAL('order_id_seq', 5000)
SETVAL('seq_name', n, false) 设置值但下次 NEXTVAL 返回 n SETVAL('order_id_seq', 5000, false)

▶ 示例:NEXTVAL 生成编号

SQL
INSERT INTO orders (order_id, customer_id, total_amount, order_date)
VALUES (NEXTVAL('order_id_seq'), 1001, 299.99, CURRENT_DATE);

输出:

TEXT 📖 仅展示
INSERT 0 1

▶ 示例:CURRVAL 查看当前值

SQL
SELECT
  NEXTVAL('report_seq') AS report_number,
  CURRVAL('report_seq') AS current_seq;

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) SETVAL 重置序列

调用 下次 NEXTVAL 返回
SETVAL('seq', 100) 101
SETVAL('seq', 100, false) 100
SETVAL('seq', 1, false) 1(重置到起点)

▶ 示例:SETVAL 重置序列

SQL
-- Reset sequence to start from 1 again
SELECT SETVAL('order_id_seq', 1, false);

-- Set sequence to a specific high value (e.g., after data migration)
SELECT SETVAL('order_id_seq', (SELECT MAX(order_id) FROM orders));

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

8. 流程图:函数选择决策

100%
flowchart TD
    A[需要什么操作?] --> B{数据类型?}
    B -->|数值| C{需要什么?}
    B -->|字符串| D{需要什么?}
    B -->|日期| E{需要什么?}
    B -->|NULL处理| F[COALESCE / NULLIF]
    C -->|四舍五入| G[ROUND / TRUNC]
    C -->|取整| H[CEIL / FLOOR]
    C -->|计算| I[ABS / MOD / POWER / SQRT]
    D -->|拼接| J[CONCAT / ||]
    D -->|截取| K[SUBSTRING / LEFT / RIGHT]
    D -->|替换| L[REPLACE / SPLIT_PART]
    D -->|大小写| M[LOWER / UPPER / INITCAP]
    D -->|修剪| N[TRIM / LTRIM / RTRIM]
    E -->|当前时间| O[NOW / CURRENT_TIMESTAMP]
    E -->|提取部分| P[EXTRACT]
    E -->|截断| Q[DATE_TRUNC]
    E -->|格式化| R[TO_CHAR]
    E -->|解析| S[TO_DATE / TO_TIMESTAMP]

    style A fill:#e1f5fe
    style F fill:#c8e6c9

9. 综合示例

Alice 生成了完整的月度销售报表。

SQL
-- Step 1: Monthly revenue report with date formatting
SELECT
  TO_CHAR(DATE_TRUNC('month', order_date), 'YYYY-Month') AS report_month,
  COUNT(*) AS order_count,
  SUM(total_amount)::NUMERIC(12,2) AS gross_revenue,
  SUM(total_amount * 0.08)::NUMERIC(12,2) AS tax_collected,
  SUM(total_amount * 1.08)::NUMERIC(12,2) AS total_with_tax
FROM orders
WHERE order_status = 'completed'
  AND order_date >= DATE_TRUNC('year', CURRENT_DATE)
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY report_month;

-- Step 2: Product summary with string formatting and NULL handling
SELECT
  LPAD(p.product_id::TEXT, 6, '0') AS product_code,
  INITCAP(LOWER(p.product_name)) AS display_name,
  CONCAT('$', ROUND(p.unit_price, 2)) AS price_label,
  COALESCE(p.discount_rate, 0) AS discount,
  ROUND(p.unit_price * (1 - COALESCE(p.discount_rate, 0)), 2) AS net_price,
  LEAST(ROUND(p.unit_price * (1 - COALESCE(p.discount_rate, 0)), 2), 999.99) AS capped_price
FROM products p
WHERE p.is_active = true
  AND p.unit_price > NULLIF(p.cost_price, 0)
ORDER BY net_price DESC;

-- Step 3: Customer order summary with age calculation
SELECT
  CONCAT(c.first_name, ' ', c.last_name) AS customer_name,
  COUNT(o.order_id) AS total_orders,
  SUM(o.total_amount)::NUMERIC(12,2) AS lifetime_value,
  AGE(MAX(o.order_date), MIN(o.order_date)) AS customer_span,
  EXTRACT(YEAR FROM AGE(CURRENT_DATE, c.register_date)) AS years_since_join
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id
WHERE o.order_status = 'completed'
GROUP BY c.customer_id, c.first_name, c.last_name, c.register_date
ORDER BY lifetime_value DESC
FETCH FIRST 25 ROWS ONLY;

❓ 常见问题

Q CONCAT 和 || 有什么区别?
A CONCAT 忽略 NULL(返回非 NULL 部分的拼接),|| 遇 NULL 则整个结果为 NULL。需要 NULL 安全拼接时用 CONCAT 或 CONCAT_WS。
Q ROUND(2.5) 结果是 2 还是 3?
A PostgreSQL 的 ROUND 使用"银行家舍入"(round half to even),ROUND(2.5) = 2,ROUND(3.5) = 4。这与某些语言的四舍五入不同。
Q EXTRACT(DOW FROM date) 周日是 0 还是 7?
A DOW 返回 0-6,0 表示周日,1 表示周一,6 表示周六。如果需要 ISO 标准(1=周一,7=周日),用 EXTRACT(ISODOW FROM date)。
Q DATE_TRUNC 能截断到小时吗?
A 可以。DATE_TRUNC 支持 microsecond/millisecond/second/minute/hour/day/week/month/quarter/year/decade/century/millennium 等多种精度。
Q COALESCE 可以接受不同类型吗?
A 可以,但所有参数必须能隐式转换为同一类型。如 COALESCE(NULL, 0, 0.5) 会将所有值转为 NUMERIC。
Q CURRVAL 在没有调用 NEXTVAL 前能使用吗?
A 不能。CURRVAL 要求当前会话已经对该序列调用过 NEXTVAL,否则会报错 "currval of sequence is not yet defined in this session"。
Q TO_CHAR 格式化数字时 FM 前缀什么意思?
A FM(Fill Mode)去除前导零和尾随空格的填充。TO_CHAR(42, 'FM00000') = '42'(不带前导零),TO_CHAR(42, '00000') = '00042'。
Q AGE 和减法运算计算日期差有什么区别?
A DATE1 - DATE2 返回整数天数;AGE(DATE1, DATE2) 返回 INTERVAL 类型,包含年月日(如 1 year 2 mons 3 days),更人性化但计算不如减法精确。

📖 小节


📝 作业

  1. ⭐ 编写查询,从 products 表中选择 product_nameunit_price,用 CONCAT 生成价格标签(格式 $99.99),用 ROUND 保留两位小数,按价格降序排列。

  2. ⭐⭐ 编写查询,生成 2025 年月度销售报表:用 DATE_TRUNC('month', order_date) 按月分组,用 TO_CHAR 格式化月份为 YYYY-MM,计算每月订单数、总金额(NUMERIC(12,2)),用 COALESCE 处理可能的 NULL 折扣。

  3. ⭐⭐⭐ 编写查询,为每个客户生成报表行:用 CONCAT_WS 拼接姓名,用 AGE 计算客户注册时长,用 EXTRACT 提取注册年份,用 GREATEST 将折扣上限限制为 50%,用 NULLIF 防止除零计算客单价,用 LPAD 格式化客户 ID 为 6 位编号,最后用 NEXTVAL('report_seq') 生成报表行序号。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏