PostgreSQL 表达式

PostgreSQL 表达式(Expressions)完全指南

表达式 是 PostgreSQL 中 可以计算出值的任何组合,包括常量、列名、函数、运算符、子查询等。
它是 SELECTWHEREORDER BYUPDATEINSERT 等语句的核心。


一、什么是表达式?

-- 这些都是表达式
5 + 3
UPPER(username)
age > 18
now() - created_at
(SELECT COUNT(*) FROM orders WHERE user_id = u.id)

表达式 = 可求值的单元,最终返回 标量值(单个值)


二、表达式分类

类型示例说明
常量表达式'hello'100true固定值
列引用usernamesalary * 1.1表中的列
算术表达式price * qty+ - * / % ^
函数调用now()COALESCE(email, 'N/A')内置或自定义函数
条件表达式CASE WHEN ... THEN ... END逻辑判断
子查询表达式(SELECT MAX(salary) FROM users)标量子查询
类型转换age::TEXT::CAST()
集合表达式ARRAY[1,2,3]数组构造

三、核心语法与示例

1. 算术表达式

SELECT 
    salary,
    salary * 1.1 AS bonus_10,
    salary / 12 AS monthly,
    salary % 1000 AS remainder
FROM users;

2. 字符串表达式

SELECT 
    username,
    UPPER(username) AS shout,
    'User: ' || username || ' (ID: ' || id || ')' AS label,
    LENGTH(username) AS name_len
FROM users;

3. 条件表达式(CASE)

SELECT 
    username,
    salary,
    CASE 
        WHEN salary > 100000 THEN 'High'
        WHEN salary > 50000 THEN 'Medium'
        ELSE 'Low'
    END AS salary_level,
    CASE WHEN is_active THEN 'Active' ELSE 'Inactive' END AS status
FROM users;

4. NULL 处理表达式

SELECT 
    email,
    COALESCE(email, 'No email') AS safe_email,
    NULLIF(username, 'admin') AS non_admin  -- 如果是 admin 返回 NULL
FROM users;

5. 日期时间表达式

SELECT 
    created_at,
    now() - created_at AS age,
    DATE_TRUNC('day', created_at) AS day,
    EXTRACT(YEAR FROM created_at) AS year,
    created_at + INTERVAL '30 days' AS future
FROM users;

6. JSON 表达式

SELECT 
    profile,
    profile -> 'settings' ->> 'theme' AS theme,
    profile @> '{"level": 5}' AS is_senior,
    jsonb_build_object('name', username, 'dept', department) AS info
FROM users;

7. 数组表达式

SELECT 
    tags,
    tags[1] AS first_tag,
    ARRAY_LENGTH(tags, 1) AS tag_count,
    tags || '{"new"}' AS added,
    ARRAY_AGG(username) OVER (PARTITION BY department) AS dept_users
FROM users;

8. 子查询表达式(标量子查询)

SELECT 
    username,
    salary,
    (SELECT AVG(salary) FROM users) AS global_avg,
    salary > (SELECT AVG(salary) FROM users WHERE department = u.department) AS above_dept_avg
FROM users u;

9. 类型转换表达式

SELECT 
    id::TEXT || '-' || username AS code,
    CAST(salary AS INTEGER) AS int_salary,
    age::TEXT AS age_str
FROM users;

四、表达式在各语句中的使用

语句表达式位置示例
SELECT选择列表SELECT salary * 1.1
WHERE条件WHERE age > 18 AND UPPER(username) LIKE 'A%'
ORDER BY排序ORDER BY salary DESC, LENGTH(username)
UPDATE赋值UPDATE users SET salary = salary * 1.05
INSERTINSERT INTO logs VALUES (now(), 'login')
HAVING分组过滤HAVING COUNT(*) > 5
GROUP BY分组键GROUP BY DATE_TRUNC('month', created_at)

五、生成列(Generated Columns)—— 表达式持久化

ALTER TABLE users 
ADD COLUMN monthly_salary NUMERIC 
GENERATED ALWAYS AS (salary / 12) STORED;

-- 查询
SELECT username, monthly_salary FROM users;

类型

  • STORED:物理存储,可索引
  • VIRTUAL:计算时生成(PG 不支持)

六、表达式索引(Expression Index)—— 性能提升

-- 加速忽略大小写搜索
CREATE INDEX idx_users_lower_name ON users(LOWER(username));

-- 加速 JSON 查询
CREATE INDEX idx_users_level ON users((profile ->> 'level'));

-- 加速函数查询
CREATE INDEX idx_users_year ON users(EXTRACT(YEAR FROM created_at));

七、常见函数 + 表达式组合

函数用途示例
COALESCE(a,b,c)第一个非 NULLCOALESCE(email, phone, 'N/A')
NULLIF(a,b)a=b 返回 NULLNULLIF(score, 0)
GREATEST(a,b)最大值GREATEST(salary, bonus)
LEAST(a,b)最小值LEAST(price1, price2)
GENERATE_SERIES()生成序列SELECT GENERATE_SERIES(1,10)
ROW_NUMBER() OVER (...)窗口函数ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC)

八、表达式性能优化

技巧说明
避免在 WHERE 中用函数包裹列错:WHERE UPPER(name) = 'ALICE' → 无法用索引
对:WHERE name = 'alice' + COLLATE 或表达式索引
使用表达式索引CREATE INDEX ON users(LOWER(email));
子查询改 JOIN标量子查询有时慢
避免重复计算用 CTE 或 WITH
-- 慢
SELECT * FROM users WHERE LENGTH(username) > 10;

-- 快:建索引
CREATE INDEX idx_users_name_len ON users(LENGTH(username));

九、表达式在视图中的应用

CREATE VIEW user_summary AS
SELECT 
    username,
    salary,
    CASE WHEN salary > 80000 THEN 'High' ELSE 'Normal' END AS level,
    COALESCE(email, 'No email') AS contact
FROM users;

-- 查询视图
SELECT * FROM user_summary WHERE level = 'High';

十、表达式实战:复杂业务逻辑

-- 1. 用户等级 + 奖励
SELECT 
    username,
    salary,
    CASE 
        WHEN salary >= 100000 THEN 'S'
        WHEN salary >= 70000 THEN 'A'
        WHEN salary >= 50000 THEN 'B'
        ELSE 'C'
    END AS grade,
    salary * CASE 
        WHEN EXTRACT(MONTH FROM now()) = 12 THEN 2.0  -- 双薪
        ELSE 1.1 
    END AS bonus
FROM users;

-- 2. 动态 JSON 构建
SELECT 
    username,
    jsonb_build_object(
        'id', id,
        'name', username,
        'active', is_active,
        'tags', tags
    ) AS user_info
FROM users;

十一、表达式错误与调试

错误原因解决
operator does not exist: text + integer类型不匹配username || id::TEXT
function upper(boolean) does not exist函数参数错UPPER(username::TEXT)
division by zero除数为 0NULLIF(denominator, 0)
subquery returns more than one row标量子查询返回多行LIMIT 1 或聚合

十二、最佳实践 Checklist

项目建议
类型一致避免隐式转换
NULL 安全COALESCENULLIF
可读性复杂表达式用 ()
性能避免在列上用函数
索引常用表达式建索引
生成列频繁计算字段用 GENERATED ALWAYS

十三、一键生成 100 条测试数据(含表达式)

INSERT INTO users (username, email, age, salary, department, is_active)
SELECT 
    'user_' || n,
    'user_' || n || '@test.com',
    (random() * 50 + 18)::INT,
    (random() * 100000 + 30000)::NUMERIC(10,2),
    CASE WHEN n % 3 = 0 THEN 'IT' ELSE 'HR' END,
    n % 5 != 0
FROM generate_series(1, 100) AS n;

十四、快速上手:5 分钟掌握表达式

-- 1. 算术 + 字符串
SELECT username, salary * 1.1 AS bonus, 'Bonus: $' || (salary * 0.1) FROM users;

-- 2. 条件 + NULL
SELECT COALESCE(email, phone, 'N/A'), 
       CASE WHEN is_active THEN 'Yes' ELSE 'No' END 
FROM users;

-- 3. JSON + 数组
SELECT profile ->> 'level', tags[1] FROM users;

-- 4. 子查询 + 窗口
SELECT username, salary,
       (SELECT AVG(salary) FROM users) AS avg,
       RANK() OVER (ORDER BY salary DESC)
FROM users;

现在就动手
users 表中:

  1. 查询 salary * 1.1usernamea 开头(忽略大小写)的用户
  2. CASEage < 30 的用户打上 Young 标签
  3. 查询 profilelevel > 5tags 包含 dev 的用户
  4. 添加生成列 year_of_birth = EXTRACT(YEAR FROM now()) - age

需要我生成:

  • 1000 条复杂测试数据(含 JSON/数组)?回复 测试数据
  • 动态分区表达式建表示例?回复 分区表达式
  • Python 中使用表达式查询?回复 Python 表达式
  • 表达式 vs 函数性能对比?回复 性能对比

随时告诉我!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注