PostgreSQL 教程

PostgreSQL 运算符

运算符(Operators)是 PostgreSQL 中的特殊符号或关键字,用于对一个或多个值(称为操作数)执行操作并生成结果。

在构建过滤、操作和组合数据库中数据的表达式和查询时,运算符是不可或缺的基础工具。它们允许你定义条件、执行计算以及比较数值,从而让你的 SQL 查询变得动态且强大。

PostgreSQL 支持多种类别的运算符,每种都针对特定类型的操作而设计。

1. 算术运算符 (Arithmetic Operators)

算术运算符用于对数值执行数学计算,包括加、减、乘、除和取模操作。

  • 加法 (+): 将两个操作数相加。
    • 示例: SELECT 10 + 5; 结果为 15
    • 实际业务场景: 计算订单中所有商品的总价。如果客户买了一件 25 美元的衬衫和一条 40 美元的裤子,总成本就是 25 + 40 = 65
    • 假设场景: 物流公司计算两个包裹的总重量。A 包裹重 15.5 kg,B 包裹重 7.2 kg,运费计算的总重量为 15.5 + 7.2 = 22.7 kg。
  • 减法 (-): 从第一个操作数中减去第二个操作数。
    • 示例: SELECT 10 - 5; 结果为 5
    • 实际业务场景: 通过从收入中减去销货成本来确定利润率。如果产品售价 100 美元,生产成本 30 美元,利润就是 100 - 30 = 70
    • 假设场景: 学校计算购买学习用品后的剩余预算。初始预算是 5000 美元,买教材花了 1200 美元,剩余预算为 5000 - 1200 = 3800
  • 乘法 (*): 将两个操作数相乘。
    • 示例: SELECT 10 * 5; 结果为 50
    • 实际业务场景: 计算多件同款商品的总价。单价 12 美元,客户买了 3 件,总价为 12 * 3 = 36
    • 假设场景: 投资公司预测投资的未来价值。如果 10,000 美元的初始投资每年增长 1.05 倍,一年后的价值为 10000 * 1.05 = 10500
  • 除法 (/): 第一个操作数除以第二个操作数。
    • 示例: SELECT 10 / 5; 结果为 2。注意: 整数除法会截断小数部分。要进行浮点除法,请确保至少有一个操作数是浮点数(例如,10.0 / 3)。
    • 实际业务场景: 计算学生的平均分。如果 5 名学生的总分是 450 分,平均分就是 450 / 5 = 90。
    • 假设场景: 工厂确定一定量的原材料可以生产多少批产品。如果有 1500 kg 材料,每批需要 250 kg,可生产的批次为 1500 / 250 = 6
  • 取模 (%): 返回除法运算后的余数。
    • 示例: SELECT 10 % 3; 结果为 1
    • 实际业务场景: 判断一个数字是奇数还是偶数(如果 数字 % 2 = 0 则是偶数)。也常用于循环操作,比如已知经过的天数计算星期几。
    • 假设场景: 调度系统将任务分配到不同的班次。如果有 3 个班次且任务按顺序编号,task_number % 3 可以决定任务落入哪个班次。

2. 比较运算符 (Comparison Operators)

比较运算符用于比较两个表达式并返回一个布尔结果(TRUEFALSENULL)。它们常用于 WHERE 子句中以过滤数据。

  • 等于 (=): 检查两个操作数是否相等。
    • 示例: SELECT 'apple' = 'apple'; 结果为 TRUESELECT 10 = 5; 结果为 FALSE
    • 实际用途: 查找在特定部门工作的所有员工 (WHERE department_id = 101)。
  • 不等于 (<>!=): 检查两个操作数是否不相等。
    • 示例: SELECT 'apple' <> 'orange'; 结果为 TRUE
    • 实际用途: 检索所有未缺货的产品 (WHERE status <> 'out_of_stock')。
  • 大于 (>): 检查第一个操作数是否大于第二个。
    • 示例: SELECT 10 > 5; 结果为 TRUE
    • 实际用途: 列出总金额大于 100 美元的所有订单 (WHERE total_amount > 100)。
  • 小于 (<): 检查第一个操作数是否小于第二个。
    • 示例: SELECT 5 < 10; 结果为 TRUE
    • 实际用途: 查找所有 30 岁以下的员工 (WHERE age < 30)。
  • 大于或等于 (>=): 检查第一个操作数是否大于或等于第二个。
    • 示例: SELECT 10 >= 10; 结果为 TRUE
    • 实际用途: 筛选消费了 500 美元及以上的客户 (WHERE total_spent >= 500)。
  • 小于或等于 (<=): 检查第一个操作数是否小于或等于第二个。
    • 示例: SELECT 5 <= 10; 结果为 TRUE
    • 实际用途: 识别价格在 20 美元及以下的产品 (WHERE price <= 20)。

3. 逻辑运算符 (Logical Operators)

逻辑运算符用于组合或修改布尔表达式。它们是在 WHEREHAVING 子句中创建复杂过滤条件的基石。

  • AND(与): 如果两个操作数均为 TRUE,则返回 TRUE
    • 示例: SELECT (10 > 5) AND (3 < 7); 结果为 TRUE
    • 实际用途: 查找既有库存且价格低于 50 美元的产品 (WHERE quantity > 0 AND price < 50)。
  • OR(或): 如果至少有一个操作数为 TRUE,则返回 TRUE
    • 示例: SELECT (10 > 5) OR (3 > 7); 结果为 TRUE
    • 实际用途: 筛选来自纽约或加州的用户 (WHERE city = 'New York' OR city = 'California')。
  • NOT(非): 反转操作数的布尔值。NOT TRUE 变为 FALSENOT FALSE 变为 TRUE
    • 示例: SELECT NOT (10 < 5); 结果为 TRUE
    • 实际用途: 检索所有尚未发货的订单 (WHERE NOT status = 'shipped')。

4. 字符串运算符 (String Operators)

PostgreSQL 提供了专门用于操作和比较文本字符串的运算符。

  • 拼接 (||): 将两个或多个字符串连接在一起。
    • 示例: SELECT 'Hello' || ' ' || 'World'; 结果为 'Hello World'
    • 实际业务场景: 将名字和姓氏合并为全名以在报表中显示。如果 first_name 是 'John',last_name 是 'Doe',则拼接 'John' || ' ' || 'Doe' 结果为 'John Doe'。
  • 模式匹配 (LIKE, ILIKE): 用于将文本值与指定的模式进行匹配。
    • LIKE区分大小写的。
    • ILIKE不区分大小写的(PostgreSQL 特有)。
    • 模式匹配使用两个特殊的通配符:
      • %:匹配任何零个或多个字符的序列。
      • _:匹配任何单个字符。
    • 示例 (LIKE): SELECT 'apple' LIKE 'app%'; 结果为 TRUESELECT 'Apple' LIKE 'app%'; 结果为 FALSE
    • 示例 (ILIKE): SELECT 'Apple' ILIKE 'app%'; 结果为 TRUE
    • 实际用途: 搜索所有姓氏以 'Sm' 开头的客户 (WHERE last_name LIKE 'Sm%'),或查找描述中任何位置包含 'widget' 的产品 (WHERE description ILIKE '%widget%')。
  • 正则表达式匹配 (~, ~*): 提供更强大的正则表达式模式匹配功能。
    • ~:区分大小写匹配。
    • ~*:不区分大小写匹配。
    • 示例 (~)SELECT 'foobar' ~ 'foo.*'; 结果为 TRUESELECT 'Foobar' ~ 'foo.*'; 结果为 FALSE
    • 示例 (~*)SELECT 'Foobar' ~* 'foo.*'; 结果为 TRUE
    • 实际用途: 验证电子邮件地址或从复杂的日志数据中提取特定模式。

5. 其他重要运算符 (Other Important Operators)

  • IS NULL / IS NOT NULL: 检查 NULL 值。NULL 代表缺失或未知的数据,不能使用像 =<> 这样的标准比较运算符来进行比较。
    • 示例SELECT NULL IS NULL; 结果为 TRUESELECT 5 IS NOT NULL; 结果为 TRUE
    • 实际用途: 识别尚未分配送货司机的订单 (WHERE driver_id IS NULL)。
  • BETWEEN: 测试某个值是否在指定范围内(包含边界值)。
    • 语法value BETWEEN low AND high
    • 示例SELECT 15 BETWEEN 10 AND 20; 结果为 TRUE
    • 实际用途: 查找价格在 20 到 50 美元之间的产品 (WHERE price BETWEEN 20 AND 50)。
  • IN: 测试某个值是否与值列表中的任何一个匹配。
    • 语法value IN (value1, value2, ...)
    • 示例SELECT 'apple' IN ('apple', 'orange', 'banana'); 结果为 TRUE
    • 实际用途: 检索特定客户 ID 列表下达的订单 (WHERE customer_id IN (1, 5, 9))。
  • NOT IN: 测试某个值是否与值列表中的任何一个都匹配。
    • 示例:SELECT 'grape' NOT IN ('apple', 'orange', 'banana'); 结果为 TRUE

6. 运算符优先级 (Operator Precedence)

运算符优先级决定了表达式中运算的执行顺序。就像在标准数学中一样,乘除法先于加减法执行。可以使用括号 () 来覆盖默认的优先级。

理解优先级对于确保表达式按预期计算至关重要。

  1. 最高优先级: 括号 ()
  2. 其次: 一元运算符(例如,正负号 +, -
  3. 其次: 乘法 *,除法 /,取模 %
  4. 其次: 加法 +,减法 -
  5. 其次: 字符串拼接 ||
  6. 其次: 比较运算符 =, <>, >, <, >=, <=
  7. 最低优先级(逻辑运算符): NOT, AND, OR

示例: SELECT 5 + 3 * 2; 如果没有括号,计算顺序为 5 + (3 * 2) = 5 + 6 = 11。如果你想要先计算加法,需使用括号:SELECT (5 + 3) * 2; 结果为 8 * 2 = 16

7. 实际案例与演示

假设我们有一个名为 products(产品)的表,包含以下结构和数据:

CREATE TABLE products (
    product_id SERIAL PRIMARY KEY,
    product_name VARCHAR(100),
    category VARCHAR(50),
    price DECIMAL(10, 2),
    stock_quantity INTEGER,
    last_updated DATE
);

INSERT INTO products (product_name, category, price, stock_quantity, last_updated) VALUES
('Laptop Pro', 'Electronics', 1200.00, 50, '2023-10-20'),
('Gaming Mouse', 'Electronics', 75.50, 200, '2023-10-18'),
('Mechanical Keyboard', 'Electronics', 120.00, 150, '2023-10-21'),
('Desk Chair Ergo', 'Furniture', 300.00, 30, '2023-10-15'),
('Monitor 27-inch', 'Electronics', 250.00, 80, '2023-10-22'),
('Wireless Earbuds', 'Audio', 99.99, 300, '2023-10-19'),
('Office Desk L-shape', 'Furniture', 450.00, 20, '2023-10-16'),
('USB-C Hub', 'Accessories', 35.00, 100, '2023-10-20'),
('External SSD 1TB', 'Storage', 150.00, 70, '2023-10-17');

7.1 使用算术运算符

计算每种产品的库存总价值:

SELECT
    product_name,
    price,
    stock_quantity,
    price * stock_quantity AS total_stock_value -- 乘法运算符
FROM
    products;

此查询使用 * 运算符将每种产品的 price 乘以 stock_quantity,得出可用库存的总货币价值。

将所有 'Electronics'(电子产品)的价格提高 10%:

UPDATE products
SET price = price * 1.10 -- 用于增加 10% 的乘法运算符
WHERE category = 'Electronics';

-- 验证更改(在执行 UPDATE 后运行)
SELECT product_name, price FROM products WHERE category = 'Electronics';

7.2 使用比较和逻辑运算符

查找价格超过 100 美元的 'Electronics'(电子产品):

SELECT
    product_name,
    category,
    price
FROM
    products
WHERE
    category = 'Electronics' AND price > 100.00; -- 结合 = 和 >,并使用 AND 连接

列出库存数量在 50 到 150 之间(含)的产品:

SELECT
    product_name,
    stock_quantity
FROM
    products
WHERE
    stock_quantity BETWEEN 50 AND 150; -- BETWEEN 运算符

识别属于 'Furniture'(家具)或 'Audio'(音频)类别的产品:

SELECT
    product_name,
    category
FROM
    products
WHERE
    category IN ('Furniture', 'Audio'); -- IN 运算符

7.3 使用字符串运算符

拼接产品名称和类别以便于展示:

SELECT
    product_id,
    product_name || ' (' || category || ')' AS product_details -- 拼接运算符
FROM
    products;

|| 运算符将产品名称、带括号的字面量字符串和类别组合成一个描述性更强的单一字符串。

查找名称中包含 'Desk' 的产品(不区分大小写):

SELECT
    product_name
FROM
    products
WHERE
    product_name ILIKE '%Desk%'; -- ILIKE 结合两端的 % 通配符

这将返回 'Desk Chair Ergo' 和 'Office Desk L-shape'。

7.4 使用 IS NULL / IS NOT NULL

假设对于新添加的产品,last_updated 字段有时可能为 NULL。首先,我们更新一个产品使其 last_updatedNULL 作为演示:

UPDATE products
SET last_updated = NULL
WHERE product_name = 'USB-C Hub';

-- 现在,检索 last_updated 日期为 NULL 的产品:
SELECT
    product_name,
    last_updated
FROM
    products
WHERE
    last_updated IS NULL; -- IS NULL 运算符

此查询能正确识别出 'USB-C Hub' 具有 NULL 值的更新日期。