Skip to content

PostgreSQL SELECT

概述

SELECT语句用于从一个或多个表中查询数据。它是最常用的SQL语句,是PostgreSQL中数据检索的基础。

基本SELECT语法

sql
SELECT column1, column2, ...
FROM table_name;

选择所有列

sql
-- 选择所有列
SELECT * FROM users;

-- 从特定表选择所有列
SELECT * FROM products;

选择特定列

sql
-- 选择特定列
SELECT name, email FROM users;

-- 选择多个列
SELECT id, name, email, created_at FROM users;

列别名

sql
-- 使用AS关键字
SELECT name AS customer_name, email AS contact_email FROM users;

-- 不使用AS关键字
SELECT name customer_name, email contact_email FROM users;

-- 带空格的别名(使用引号)
SELECT name AS "客户名称", email AS "电子邮件地址" FROM users;

SELECT中的表达式

sql
-- 算术运算
SELECT product_name, price, price * 1.1 AS price_with_tax FROM products;

-- 字符串连接
SELECT first_name || ' ' || last_name AS full_name FROM users;

-- 使用函数
SELECT UPPER(name) AS name_upper, LENGTH(email) AS email_length FROM users;

DISTINCT - 去除重复

什么是 DISTINCT?

DISTINCT 关键字用于从查询结果中删除重复行,只返回指定列的唯一值。

基本语法

sql
SELECT DISTINCT column1, column2, ...
FROM table_name;

单列 DISTINCT

sql
-- 去除重复
SELECT DISTINCT country FROM users;

-- 获取唯一的产品类别
SELECT DISTINCT category FROM products ORDER BY category;

-- 统计唯一值数量
SELECT COUNT(DISTINCT customer_id) AS unique_customers FROM orders;
SELECT COUNT(DISTINCT product_id) AS unique_products FROM order_items;

多列 DISTINCT

当对多列使用 DISTINCT 时,PostgreSQL 会根据所有指定列的组合来判断唯一性。

sql
-- 多列去重
SELECT DISTINCT status, country FROM users;

-- 获取城市和州的唯一组合
SELECT DISTINCT city, state FROM customers ORDER BY state, city;

-- 获取产品名称和类别的唯一组合
SELECT DISTINCT product_name, category FROM products;

DISTINCT 与聚合函数

sql
-- 统计唯一客户数
SELECT COUNT(DISTINCT customer_id) AS unique_customers FROM orders;

-- 统计每个类别中的唯一产品数
SELECT 
    category,
    COUNT(DISTINCT product_id) AS unique_products
FROM products
GROUP BY category;

-- 多个不同计数
SELECT 
    COUNT(DISTINCT customer_id) AS unique_customers,
    COUNT(DISTINCT product_id) AS unique_products,
    COUNT(*) AS total_orders
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id;

DISTINCT ON(PostgreSQL 特有)

DISTINCT ON 为指定列的每个唯一值返回第一行。

sql
-- 获取每个部门的第一个员工
SELECT DISTINCT ON (department) 
    department, 
    employee_name, 
    salary
FROM employees
ORDER BY department, salary DESC;

-- 获取每个客户的最新订单
SELECT DISTINCT ON (customer_id)
    customer_id,
    order_id,
    order_date,
    total_amount
FROM orders
ORDER BY customer_id, order_date DESC;

-- 获取每个类别中价格最高的产品
SELECT DISTINCT ON (category)
    category,
    product_name,
    price
FROM products
ORDER BY category, price DESC;

DISTINCT 性能优化

sql
-- 1. 在常用列上创建索引
CREATE INDEX idx_customers_city ON customers(city);
SELECT DISTINCT city FROM customers;

-- 2. DISTINCT vs GROUP BY
-- 使用 DISTINCT
SELECT DISTINCT category FROM products;

-- 使用 GROUP BY(有索引时可能更快)
SELECT category FROM products GROUP BY category;

-- 3. 避免在大结果集上使用 DISTINCT
-- 低效
SELECT DISTINCT * FROM large_table;

-- 更好:明确指定列
SELECT DISTINCT column1, column2 FROM large_table;

DISTINCT 与 JOIN

sql
-- 获取购买了特定产品的唯一客户
SELECT DISTINCT c.customer_id, c.customer_name
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
WHERE oi.product_id IN (1, 2, 3);

-- 获取上个月售出的唯一产品类别
SELECT DISTINCT p.category
FROM products p
JOIN order_items oi ON p.product_id = oi.product_id
JOIN orders o ON oi.order_id = o.order_id
WHERE o.order_date >= CURRENT_DATE - INTERVAL '1 month';

NULL 值处理

DISTINCT 将 NULL 值视为相等:

sql
-- 如果多行在列中有 NULL,只返回一个 NULL
SELECT DISTINCT phone_number FROM customers;

-- 排除 NULL
SELECT DISTINCT phone_number 
FROM customers 
WHERE phone_number IS NOT NULL;

常见错误

sql
-- 错误1:不必要地使用 DISTINCT
SELECT DISTINCT id FROM users;  -- 主键已经唯一

-- 错误2:DISTINCT 与 ORDER BY
-- 错误:ORDER BY 列必须在 SELECT 中
SELECT DISTINCT city FROM customers ORDER BY state;

-- 正确:包含所有 ORDER BY 列
SELECT DISTINCT city, state FROM customers ORDER BY state, city;

计算字段

sql
-- 数学计算
SELECT 
    product_name,
    price,
    quantity,
    price * quantity AS total_value
FROM inventory;

-- 条件表达式
SELECT 
    name,
    age,
    CASE 
        WHEN age < 18 THEN '未成年'
        WHEN age >= 18 AND age < 65 THEN '成年'
        ELSE '老年'
    END AS age_group
FROM users;

从多个表选择

sql
-- SELECT中的简单连接
SELECT users.name, orders.order_number
FROM users, orders
WHERE users.id = orders.user_id;

-- 更好的方式:使用显式JOIN
SELECT u.name, o.order_number
FROM users u
INNER JOIN orders o ON u.id = o.user_id;

SELECT中的子查询

sql
-- 标量子查询
SELECT 
    name,
    (SELECT COUNT(*) FROM orders WHERE user_id = users.id) AS order_count
FROM users;

-- 带聚合的子查询
SELECT 
    product_name,
    price,
    (SELECT AVG(price) FROM products) AS avg_price
FROM products;

SELECT与函数

sql
-- 聚合函数
SELECT COUNT(*) FROM users;
SELECT AVG(price) FROM products;
SELECT SUM(total) FROM orders;
SELECT MIN(price), MAX(price) FROM products;

-- 字符串函数
SELECT UPPER(name), LOWER(email) FROM users;
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM users;

-- 日期函数
SELECT CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP;
SELECT name, AGE(birth_date) AS age FROM users;

SELECT与LIMIT

sql
-- 限制行数
SELECT * FROM users LIMIT 10;

-- 带偏移量的限制(分页)
SELECT * FROM users LIMIT 10 OFFSET 20;

-- 替代语法
SELECT * FROM users OFFSET 20 LIMIT 10;

SELECT与ORDER BY

sql
-- 按单列排序
SELECT * FROM users ORDER BY name;

-- 降序排序
SELECT * FROM products ORDER BY price DESC;

-- 按多列排序
SELECT * FROM users ORDER BY country, name;

-- 按表达式排序
SELECT * FROM products ORDER BY price * quantity DESC;

SELECT与WHERE

sql
-- 基本过滤
SELECT * FROM users WHERE status = 'active';

-- 多个条件
SELECT * FROM products WHERE price > 50 AND category = 'Electronics';

-- 模式匹配
SELECT * FROM users WHERE email LIKE '%@gmail.com';

-- 范围
SELECT * FROM products WHERE price BETWEEN 10 AND 100;

-- 列表
SELECT * FROM users WHERE country IN ('USA', 'UK', 'Canada');

SELECT与GROUP BY

sql
-- 分组和计数
SELECT country, COUNT(*) AS user_count
FROM users
GROUP BY country;

-- 带多个聚合的分组
SELECT 
    category,
    COUNT(*) AS product_count,
    AVG(price) AS avg_price,
    MIN(price) AS min_price,
    MAX(price) AS max_price
FROM products
GROUP BY category;

SELECT与HAVING

sql
-- 过滤分组
SELECT country, COUNT(*) AS user_count
FROM users
GROUP BY country
HAVING COUNT(*) > 100;

-- 带多个条件的HAVING
SELECT 
    category,
    AVG(price) AS avg_price
FROM products
GROUP BY category
HAVING AVG(price) > 50 AND COUNT(*) > 10;

完整SELECT示例

sql
-- 组合多个子句的复杂查询
SELECT 
    u.name,
    u.email,
    COUNT(o.id) AS order_count,
    SUM(o.total) AS total_spent,
    AVG(o.total) AS avg_order_value
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = 'active'
    AND u.created_at >= '2024-01-01'
GROUP BY u.id, u.name, u.email
HAVING COUNT(o.id) > 0
ORDER BY total_spent DESC
LIMIT 100;

最佳实践

sql
-- 1. 指定列而不是SELECT *
SELECT id, name, email FROM users;  -- 好
SELECT * FROM users;                 -- 生产环境中避免

-- 2. 使用表别名提高清晰度
SELECT u.name, o.order_number
FROM users u
INNER JOIN orders o ON u.id = o.user_id;

-- 3. 使用有意义的列别名
SELECT 
    COUNT(*) AS total_users,
    AVG(age) AS average_age
FROM users;

-- 4. 格式化以提高可读性
SELECT 
    u.id,
    u.name,
    u.email,
    COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name, u.email;

常见模式

分页

sql
-- 第1页(第1-10行)
SELECT * FROM products ORDER BY id LIMIT 10 OFFSET 0;

-- 第2页(第11-20行)
SELECT * FROM products ORDER BY id LIMIT 10 OFFSET 10;

-- 第3页(第21-30行)
SELECT * FROM products ORDER BY id LIMIT 10 OFFSET 20;

前N条记录

sql
-- 最贵的10个产品
SELECT * FROM products ORDER BY price DESC LIMIT 10;

-- 消费最多的5个客户
SELECT 
    user_id,
    SUM(total) AS total_spent
FROM orders
GROUP BY user_id
ORDER BY total_spent DESC
LIMIT 5;

条件选择

sql
-- 根据条件选择不同的列
SELECT 
    name,
    CASE 
        WHEN status = 'active' THEN email
        ELSE 'N/A'
    END AS contact
FROM users;

小结

SELECT语句是PostgreSQL的基础:

  • 基本SELECT columns FROM table
  • 过滤:使用WHERE子句
  • 排序:使用ORDER BY子句
  • 分组:使用GROUP BY与聚合
  • 限制:使用LIMIT和OFFSET
  • 别名:提高可读性
  • 函数:转换和聚合数据