PostgreSQL: PostgreSQL内置函数详解
最后更新:2026-08-26
1. 你将学到
- 数学函数:
ABS / ROUND / CEIL / FLOOR / MOD / POWER / SQRT - 字符串函数:
LENGTH / CONCAT / LOWER / UPPER / TRIM / SUBSTRING / REPLACE / SPLIT_PART / LEFT / RIGHT - 日期函数:
NOW / CURRENT_DATE / AGE / EXTRACT / DATE_TRUNC / TO_CHAR / TO_DATE - 条件函数:
COALESCE / NULLIF / GREATEST / LEAST - 序列函数:
NEXTVAL / CURRVAL / SETVAL - 函数组合使用技巧
2. 故事
Alice 是电商数据分析师,月底需要生成月度销售报表。报表需求包括:
- 日期格式化:将
order_date显示为2025-June-30格式 - 字符串拼接:客户名 + 订单号生成报表行标题
- NULL 处理:缺失的折扣值默认为 0
- 金额计算:含税总价、折扣后净额的精确计算
- 序列使用:为报表行生成连续编号
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. 流程图:函数选择决策
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),更人性化但计算不如减法精确。
📖 小节
- 数学函数:
ROUND/CEIL/FLOOR取整,ABS/MOD/POWER/SQRT计算,注意整数除法与精度 - 字符串函数:
CONCAT(NULL 安全)/||(NULL 不安全),SUBSTRING/LEFT/RIGHT截取,REPLACE/SPLIT_PART替换拆分 - 日期函数:
NOW/CURRENT_TIMESTAMP取当前时间,EXTRACT提取部分,DATE_TRUNC截断,TO_CHAR/TO_DATE格式化与解析 - 条件函数:
COALESCE返回首个非 NULL 值,NULLIF相等时返回 NULL 防除零,GREATEST/LEAST取极值 - 序列函数:
NEXTVAL递增取值,CURRVAL查当前值,SETVAL重置 - 函数组合是实际开发的核心能力,善用嵌套与类型转换
📝 作业
-
⭐ 编写查询,从
products表中选择product_name、unit_price,用CONCAT生成价格标签(格式$99.99),用ROUND保留两位小数,按价格降序排列。 -
⭐⭐ 编写查询,生成 2025 年月度销售报表:用
DATE_TRUNC('month', order_date)按月分组,用TO_CHAR格式化月份为YYYY-MM,计算每月订单数、总金额(NUMERIC(12,2)),用COALESCE处理可能的 NULL 折扣。 -
⭐⭐⭐ 编写查询,为每个客户生成报表行:用
CONCAT_WS拼接姓名,用AGE计算客户注册时长,用EXTRACT提取注册年份,用GREATEST将折扣上限限制为 50%,用NULLIF防止除零计算客单价,用LPAD格式化客户 ID 为 6 位编号,最后用NEXTVAL('report_seq')生成报表行序号。