Skip to content

PostgreSQL 比较函数和操作符详解

概述

比较操作符和函数是 PostgreSQL 中最基础且使用频率最高的功能之一。它们用于比较数据值、过滤查询结果、建立查询条件等。掌握这些操作符对于编写高效的 SQL 查询至关重要。

基本比较操作符

操作符概览

PostgreSQL 提供了完整的比较操作符集合,适用于所有具有自然顺序的数据类型:

操作符描述返回类型示例
<小于boolean5 < 10 → true
>大于boolean10 > 5 → true
<=小于或等于boolean5 <= 5 → true
>=大于或等于boolean10 >= 5 → true
=等于boolean5 = 5 → true
<>不等于(标准 SQL)boolean5 <> 3 → true
!=不等于(别名)boolean5 != 3 → true

INFO

<> 是 SQL 标准的"不等于"操作符,而 != 是一个别名,在解析阶段会被转换为 <>

实际应用示例

场景一:员工薪资查询系统

假设我们有一个员工表,需要进行各种薪资筛选:

sql
-- 创建员工表
CREATE TABLE employees (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    department VARCHAR(50),
    salary DECIMAL(10,2),
    hire_date DATE
);

-- 插入测试数据
INSERT INTO employees (name, department, salary, hire_date) VALUES
    ('张三', '技术部', 8000.00, '2020-01-15'),
    ('李四', '销售部', 6500.00, '2019-03-20'),
    ('王五', '技术部', 12000.00, '2018-06-10'),
    ('赵六', '人事部', 7000.00, '2021-02-28'),
    ('钱七', '技术部', 9500.00, '2020-08-05');

查询高薪员工(薪资大于 8000):

sql
SELECT name, department, salary
FROM employees
WHERE salary > 8000;

输出结果:

 name | department | salary
------+------------+---------
 王五  | 技术部      | 12000.00
 钱七  | 技术部      | 9500.00

分析过程:

  • salary > 8000 使用大于操作符筛选薪资超过 8000 的员工
  • 返回两条记录,符合条件的员工信息

场景二:日期范围查询

查询 2020 年入职的员工:

sql
SELECT name, hire_date
FROM employees
WHERE hire_date >= '2020-01-01' AND hire_date <= '2020-12-31';

输出结果:

 name | hire_date
------+------------
 张三  | 2020-01-15
 钱七  | 2020-08-05

跨类型比较

PostgreSQL 支持相关数据类型之间的比较:

sql
-- 整数与大整数比较
SELECT 100 > 50::bigint AS result;  -- true

-- 数值与小数比较
SELECT 10 = 10.0 AS result;         -- true

-- 文本比较(按字典序)
SELECT 'apple' < 'banana' AS result; -- true

TIP

当比较不同但相关的数据类型时,PostgreSQL 会自动将"不太通用"的类型转换为"更通用"的类型。

比较谓词

BETWEEN 范围查询

BETWEEN 是最常用的范围查询谓词,包含端点值。

语法格式

sql
expression BETWEEN low_value AND high_value
-- 等价于
expression >= low_value AND expression <= high_value

实际应用示例

查询薪资在 7000 到 10000 之间的员工:

sql
SELECT name, salary
FROM employees
WHERE salary BETWEEN 7000 AND 10000;

输出结果:

 name | salary
------+--------
 张三  | 8000.00
 赵六  | 7000.00
 钱七  | 9500.00

使用 NOT BETWEEN:

sql
SELECT name, salary
FROM employees
WHERE salary NOT BETWEEN 7000 AND 10000;

输出结果:

 name | salary
------+---------
 李四  | 6500.00
 王五  | 12000.00

BETWEEN SYMMETRIC 特性

BETWEEN SYMMETRIC 会自动对端点值进行排序:

sql
-- 传统 BETWEEN(返回 false,因为 3 > 1)
SELECT 2 BETWEEN 3 AND 1 AS traditional;  -- false

-- SYMMETRIC BETWEEN(自动排序,等价于 2 BETWEEN 1 AND 3)
SELECT 2 BETWEEN SYMMETRIC 3 AND 1 AS symmetric;  -- true

WARNING

BETWEEN 语法中使用 AND 可能与逻辑运算符 AND 产生歧义。如果需要复杂的子表达式,请使用括号。

sql
-- 正确的复杂表达式写法
SELECT * FROM employees
WHERE salary BETWEEN (base_salary * 1.2) AND (base_salary * 1.8);

NULL 值处理

IS NULL 和 IS NOT NULL

NULL 值在数据库中表示"未知"或"缺失"的数据,需要特殊的处理方式。

sql
-- 添加包含 NULL 值的测试数据
INSERT INTO employees (name, department, salary, hire_date) VALUES
    ('孙八', '技术部', NULL, '2021-05-15');

-- 查询薪资未确定的员工
SELECT name, salary
FROM employees
WHERE salary IS NULL;

输出结果:

 name | salary
------+--------
 孙八  |

查询有薪资记录的员工:

sql
SELECT name, salary
FROM employees
WHERE salary IS NOT NULL;

DANGER

错误用法: 永远不要使用 expression = NULL

sql
-- 错误!这总是返回 NULL,而不是 true 或 false
SELECT name FROM employees WHERE salary = NULL;

-- 正确!使用 IS NULL
SELECT name FROM employees WHERE salary IS NULL;

IS DISTINCT FROM 和 IS NOT DISTINCT FROM

这两个谓词将 NULL 视为可比较的普通值:

sql
-- 创建测试表
CREATE TABLE test_nulls (
    id INT,
    value INT
);

INSERT INTO test_nulls VALUES
    (1, 10),
    (2, NULL),
    (3, 10),
    (4, NULL);

-- 标准等于比较(NULL 参与时返回 NULL)
SELECT id, value = 10 AS standard_eq
FROM test_nulls;

输出结果:

 id | standard_eq
----+-------------
  1 | t
  2 |
  3 | t
  4 |

使用 IS DISTINCT FROM:

sql
-- 将 NULL 视为可比较值
SELECT id, value IS DISTINCT FROM 10 AS distinct_from
FROM test_nulls;

输出结果:

 id | distinct_from
----+---------------
  1 | f
  2 | t
  3 | f
  4 | t

分析过程:

  • IS DISTINCT FROM 将 NULL 视为普通值
  • 当 value 为 NULL 时,NULL IS DISTINCT FROM 10 返回 true
  • 当 value 为 10 时,10 IS DISTINCT FROM 10 返回 false

布尔值测试

PostgreSQL 提供了专门的布尔值测试谓词:

sql
-- 创建布尔测试表
CREATE TABLE bool_test (
    id INT,
    flag BOOLEAN
);

INSERT INTO bool_test VALUES
    (1, TRUE),
    (2, FALSE),
    (3, NULL);

-- 测试各种布尔谓词
SELECT
    id,
    flag,
    flag IS TRUE AS is_true,
    flag IS FALSE AS is_false,
    flag IS UNKNOWN AS is_unknown
FROM bool_test;

输出结果:

 id | flag | is_true | is_false | is_unknown
----+------+---------+----------+------------
  1 | t    | t       | f        | f
  2 | f    | f       | t        | f
  3 |      | f       | f        | t

比较函数

PostgreSQL 提供了实用的比较相关函数:

num_nonnulls() 和 num_nulls()

这些函数在数据质量检查中非常有用:

sql
-- 统计每行中非 NULL 字段的数量
SELECT
    name,
    num_nonnulls(name, department, salary, hire_date) AS non_null_count,
    num_nulls(name, department, salary, hire_date) AS null_count
FROM employees;

输出结果:

 name | non_null_count | null_count
------+----------------+------------
 张三  | 4              | 0
 李四  | 4              | 0
 王五  | 4              | 0
 赵六  | 4              | 0
 钱七  | 4              | 0
 孙八  | 3              | 1

实际应用:数据完整性检查

sql
-- 查找数据不完整的记录
SELECT
    name,
    CASE
        WHEN num_nulls(name, department, salary, hire_date) > 0
        THEN '数据不完整'
        ELSE '数据完整'
    END AS data_status
FROM employees
WHERE num_nulls(name, department, salary, hire_date) > 0;

高级应用场景

复杂条件组合

在实际业务中,我们经常需要组合多个比较条件:

sql
-- 查找技术部门中薪资高于平均水平的资深员工
SELECT
    e1.name,
    e1.salary,
    e1.hire_date
FROM employees e1
WHERE e1.department = '技术部'
  AND e1.salary > (
    SELECT AVG(salary)
    FROM employees e2
    WHERE e2.department = '技术部'
      AND e2.salary IS NOT NULL
  )
  AND e1.hire_date < '2020-01-01';

范围查询优化

使用比较操作符进行范围查询时的最佳实践:

sql
-- 创建索引以优化范围查询
CREATE INDEX idx_employees_salary ON employees(salary);
CREATE INDEX idx_employees_hire_date ON employees(hire_date);

-- 高效的范围查询
EXPLAIN ANALYZE
SELECT name, salary
FROM employees
WHERE salary BETWEEN 7000 AND 15000
  AND hire_date >= '2019-01-01';

动态条件构建

在应用程序中构建动态查询条件:

sql
-- 模拟应用程序的动态查询需求
-- 假设用户可以选择薪资范围和部门
DO $$
DECLARE
    min_salary DECIMAL := 8000;
    max_salary DECIMAL := NULL;  -- NULL 表示不限制上限
    dept VARCHAR := '技术部';
BEGIN
    -- 构建动态查询
    IF max_salary IS NULL THEN
        RAISE NOTICE '查询条件:薪资 >= % AND 部门 = %', min_salary, dept;
    ELSE
        RAISE NOTICE '查询条件:薪资 BETWEEN % AND % AND 部门 = %',
                     min_salary, max_salary, dept;
    END IF;
END $$;

性能考虑

索引优化

sql
-- 为经常用于比较的列创建索引
CREATE INDEX idx_employees_salary_dept ON employees(department, salary);

-- 部分索引:只为特定条件创建索引
CREATE INDEX idx_high_salary ON employees(salary)
WHERE salary > 10000;

查询计划分析

使用 EXPLAIN 分析比较操作的性能:

sql
-- 分析范围查询的执行计划
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM employees
WHERE salary BETWEEN 8000 AND 12000;

最佳实践总结

TIP

比较操作符使用建议

  1. 优先使用标准 SQL 语法:使用 <> 而不是 !=
  2. 正确处理 NULL 值:使用 IS NULL 而不是 = NULL
  3. 选择合适的谓词:对于范围查询优先使用 BETWEEN
  4. 索引优化:为频繁比较的列创建合适的索引
  5. 类型一致性:尽量保证比较双方的数据类型一致

WARNING

常见陷阱

  1. NULL 值比较陷阱NULL = NULL 返回 NULL 而不是 true
  2. BETWEEN 的包含性BETWEEN 包含端点值
  3. 类型转换开销:避免不必要的跨类型比较
  4. 复杂表达式:在 BETWEEN 中使用复杂表达式时要加括号

通过掌握这些比较函数和操作符,你可以编写出更加高效和准确的 PostgreSQL 查询。在实际应用中,合理使用这些工具能够显著提升数据查询和处理的效率。