文章目录
正文
1. 查询概述
数据查询是数据库中最常用的操作,它允许我们从数据库表中检索和分析数据。MySQL提供了强大而灵活的查询功能,可以满足各种业务需求。
1.1 查询的作用
- 检索所需数据
- 筛选符合条件的记录
- 对数据进行排序和分组
- 聚合和计算数据
- 将多个表的数据关联起来
1.2 MySQL查询的基本结构
MySQL查询主要基于SQL的SELECT语句,基本结构如下:
2. SELECT 语句基础
2.1 查询所有列
最基本的查询是检索表中的所有列和所有行:
-- 查询表中的所有列
SELECT * FROM customers;
-- 查询表中的指定列
SELECT first_name, last_name, email FROM customers;
2.2 列别名
可以为查询结果中的列指定别名,使输出结果更具可读性:
-- 使用AS关键字指定列别名
SELECT
product_id AS ID,
product_name AS Name,
price AS "Unit Price"
FROM products;
-- 可以省略AS关键字
SELECT
product_id ID,
product_name "Product Name",
price "Price ($)"
FROM products;
2.3 去除重复值
使用DISTINCT关键字可以去除查询结果中的重复行:
-- 查询不同的城市
SELECT DISTINCT city FROM customers;
-- 查询不同的城市和国家组合
SELECT DISTINCT city, country FROM customers;
2.4 计算字段
可以在查询中使用表达式创建计算字段:
-- 简单数学运算
SELECT
product_name,
price,
quantity,
price * quantity AS total_value
FROM products;
-- 字符串连接
SELECT
CONCAT(first_name, ' ', last_name) AS full_name,
email
FROM customers;
-- 日期计算
SELECT
order_id,
order_date,
ship_date,
DATEDIFF(ship_date, order_date) AS processing_days
FROM orders;
3. WHERE 子句与条件过滤
3.1 基本条件操作符
WHERE子句允许我们根据条件筛选数据:
-- 等于条件
SELECT * FROM products WHERE price = 19.99;
-- 不等于条件
SELECT * FROM products WHERE category_id <> 5;
-- 大于、小于条件
SELECT * FROM products WHERE price > 100;
SELECT * FROM products WHERE stock < 10;
-- 大于等于、小于等于条件
SELECT * FROM products WHERE price >= 50 AND price <= 100;
3.2 范围条件 (BETWEEN)
BETWEEN操作符用于筛选在指定范围内的值:
-- 筛选价格在50到100之间的产品
SELECT * FROM products WHERE price BETWEEN 50 AND 100;
-- 筛选日期范围
SELECT * FROM orders
WHERE order_date BETWEEN '2023-01-01' AND '2023-03-31';
-- 排除范围
SELECT * FROM products
WHERE price NOT BETWEEN 50 AND 100;
3.3 列表条件 (IN)
IN操作符用于匹配指定列表中的任何值:
-- 筛选特定分类的产品
SELECT * FROM products
WHERE category_id IN (1, 3, 5);
-- 筛选特定状态的订单
SELECT * FROM orders
WHERE status IN ('pending', 'processing', 'shipped');
-- 排除列表
SELECT * FROM products
WHERE category_id NOT IN (2, 4, 6);
3.4 模糊匹配 (LIKE)
LIKE操作符用于模式匹配,通常与通配符一起使用:
-- 筛选名字以'A'开头的客户
SELECT * FROM customers
WHERE first_name LIKE 'A%';
-- 筛选包含'phone'的产品
SELECT * FROM products
WHERE product_name LIKE '%phone%';
-- 筛选以特定模式匹配的电子邮件
SELECT * FROM customers
WHERE email LIKE '%@gmail.com';
-- 使用下划线通配符匹配单个字符
SELECT * FROM products
WHERE product_code LIKE 'AB_123';
通配符说明:
%
: 匹配任意数量的字符(包括零个字符)_
: 匹配单个字符
3.5 NULL值检查
NULL表示"无值"或"未知值",需要特殊处理:
-- 查找缺少电话号码的客户
SELECT * FROM customers
WHERE phone IS NULL;
-- 查找有电话号码的客户
SELECT * FROM customers
WHERE phone IS NOT NULL;
-- 查找没有完成日期的订单
SELECT * FROM orders
WHERE completion_date IS NULL;
3.6 逻辑运算符
可以使用逻辑运算符组合多个条件:
-- 使用AND组合条件
SELECT * FROM products
WHERE price > 100 AND stock > 0;
-- 使用OR组合条件
SELECT * FROM customers
WHERE country = 'USA' OR country = 'Canada';
-- 使用NOT取反条件
SELECT * FROM products
WHERE NOT category_id = 5;
-- 混合使用逻辑运算符(注意优先级)
SELECT * FROM products
WHERE (category_id = 1 OR category_id = 2)
AND price < 50;
4. 排序查询结果
4.1 基本排序 (ORDER BY)
ORDER BY子句用于对查询结果进行排序:
-- 按单一列升序排序(默认)
SELECT * FROM products
ORDER BY price;
-- 显式指定升序排序
SELECT * FROM products
ORDER BY price ASC;
-- 降序排序
SELECT * FROM customers
ORDER BY last_name DESC;
-- 按多列排序
SELECT * FROM customers
ORDER BY country, city, last_name;
-- 混合排序方向
SELECT * FROM products
ORDER BY category_id ASC, price DESC;
4.2 按别名排序
可以按照SELECT中定义的列别名排序:
SELECT
product_id,
product_name,
price * 1.1 AS increased_price
FROM products
ORDER BY increased_price DESC;
4.3 按位置排序
可以使用列在SELECT中的位置来排序(不推荐,可读性低):
-- 按第3列排序
SELECT product_id, product_name, price, stock
FROM products
ORDER BY 3 DESC;
4.4 NULL值排序
MySQL中,NULL值默认排在最前面:
-- NULL值在前(默认)
SELECT * FROM employees
ORDER BY manager_id;
-- NULL值在后
SELECT * FROM employees
ORDER BY manager_id IS NULL, manager_id;
5. 限制查询结果
5.1 LIMIT 子句
LIMIT子句用于限制返回的结果行数:
-- 只返回前5个客户
SELECT * FROM customers
LIMIT 5;
-- 指定偏移量和行数
SELECT * FROM customers
LIMIT 10, 5; -- 跳过前10行,返回接下来的5行
-- 替代语法
SELECT * FROM customers
LIMIT 5 OFFSET 10; -- 同上例
5.2 分页查询
通常使用LIMIT实现分页功能:
-- 第1页(每页10项)
SELECT * FROM products
ORDER BY product_name
LIMIT 0, 10;
-- 第2页(每页10项)
SELECT * FROM products
ORDER BY product_name
LIMIT 10, 10;
-- 第n页的通用公式
-- LIMIT (page_number - 1) * page_size, page_size
6. 聚合函数与分组
6.1 常用聚合函数
聚合函数对一组值执行计算并返回单一值:
-- 计数
SELECT COUNT(*) FROM customers;
SELECT COUNT(customer_id) FROM customers;
SELECT COUNT(DISTINCT country) FROM customers;
-- 求和
SELECT SUM(price * quantity) FROM order_items;
-- 平均值
SELECT AVG(price) FROM products;
-- 最大值和最小值
SELECT MAX(price), MIN(price) FROM products;
-- 组合使用多个聚合函数
SELECT
COUNT(*) AS total_products,
AVG(price) AS average_price,
MIN(price) AS lowest_price,
MAX(price) AS highest_price,
SUM(stock) AS total_inventory
FROM products;
6.2 GROUP BY 子句
GROUP BY子句将查询结果分组,然后对每个组应用聚合函数:
-- 按国家分组计算客户数量
SELECT country, COUNT(*) AS customer_count
FROM customers
GROUP BY country;
-- 按多个列分组
SELECT country, city, COUNT(*) AS customer_count
FROM customers
GROUP BY country, city;
-- 按产品类别统计价格信息
SELECT
category_id,
COUNT(*) AS product_count,
AVG(price) AS average_price,
MIN(price) AS lowest_price,
MAX(price) AS highest_price
FROM products
GROUP BY category_id;
6.3 HAVING 子句
HAVING子句用于过滤分组后的结果:
-- 找出客户数量超过5的国家
SELECT country, COUNT(*) AS customer_count
FROM customers
GROUP BY country
HAVING COUNT(*) > 5;
-- 找出平均价格超过100的产品类别
SELECT
category_id,
AVG(price) AS average_price
FROM products
GROUP BY category_id
HAVING AVG(price) > 100;
-- 结合WHERE和HAVING
SELECT
category_id,
COUNT(*) AS product_count,
AVG(price) AS average_price
FROM products
WHERE price > 10
GROUP BY category_id
HAVING COUNT(*) >= 3;
6.4 分组和排序结合
通常结合GROUP BY和ORDER BY来排序分组结果:
-- 按订单总额对客户排序
SELECT
customer_id,
COUNT(*) AS order_count,
SUM(total) AS total_spent
FROM orders
GROUP BY customer_id
ORDER BY total_spent DESC;
-- 找出销量最高的产品类别
SELECT
category_id,
SUM(quantity_sold) AS total_sold
FROM sales
GROUP BY category_id
ORDER BY total_sold DESC
LIMIT 5;
7. 查询中的子查询
7.1 子查询基础
子查询是嵌套在另一个查询内部的SELECT语句:
-- 查找高于平均价格的产品
SELECT *
FROM products
WHERE price > (
SELECT AVG(price)
FROM products
);
-- 查找订单数量最多的客户
SELECT *
FROM customers
WHERE customer_id = (
SELECT customer_id
FROM orders
GROUP BY customer_id
ORDER BY COUNT(*) DESC
LIMIT 1
);
7.2 IN 子查询
使用IN操作符与子查询结合:
-- 查找有订单的客户
SELECT *
FROM customers
WHERE customer_id IN (
SELECT DISTINCT customer_id
FROM orders
);
-- 查找有高价产品的类别
SELECT *
FROM categories
WHERE category_id IN (
SELECT category_id
FROM products
WHERE price > 1000
);
7.3 比较运算符与子查询
可以使用比较运算符与子查询结合:
-- 查找大于平均库存的产品
SELECT *
FROM products
WHERE stock > (
SELECT AVG(stock)
FROM products
);
-- 使用ANY操作符
SELECT *
FROM products
WHERE price > ANY (
SELECT price
FROM products
WHERE category_id = 5
);
-- 使用ALL操作符
SELECT *
FROM products
WHERE price > ALL (
SELECT price
FROM products
WHERE category_id = 2
);
7.4 EXISTS 子查询
EXISTS用于检查子查询是否返回任何行:
-- 查找有订单的客户
SELECT *
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);
-- 查找没有订单的客户
SELECT *
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);
7.5 相关子查询
相关子查询依赖于外部查询的数据:
-- 查找每个类别中价格最高的产品
SELECT *
FROM products p1
WHERE price = (
SELECT MAX(price)
FROM products p2
WHERE p2.category_id = p1.category_id
);
-- 查找高于其类别平均价格的产品
SELECT *
FROM products p
WHERE price > (
SELECT AVG(price)
FROM products
WHERE category_id = p.category_id
);
8. 多表联接查询
8.1 内联接 (INNER JOIN)
内联接返回两个表中匹配的行:
-- 客户及其订单
SELECT
c.customer_id,
c.first_name,
c.last_name,
o.order_id,
o.order_date,
o.total
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id;
-- 多表联接
SELECT
o.order_id,
c.first_name,
c.last_name,
p.product_name,
oi.quantity,
oi.price
FROM orders o
INNER JOIN customers c ON o.customer_id = c.customer_id
INNER JOIN order_items oi ON o.order_id = oi.order_id
INNER JOIN products p ON oi.product_id = p.product_id;
8.2 左外联接 (LEFT JOIN)
左外联接返回左表所有行,即使右表没有匹配行:
-- 所有客户及其订单(包括没有订单的客户)
SELECT
c.customer_id,
c.first_name,
c.last_name,
o.order_id,
o.order_date
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id;
-- 查找没有订单的客户
SELECT
c.customer_id,
c.first_name,
c.last_name
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_id IS NULL;
8.3 右外联接 (RIGHT JOIN)
右外联接返回右表所有行,即使左表没有匹配行:
-- 所有订单及其客户(包括没有匹配客户的订单)
SELECT
o.order_id,
o.order_date,
c.customer_id,
c.first_name,
c.last_name
FROM orders o
RIGHT JOIN customers c ON o.customer_id = c.customer_id;
8.4 自联接 (Self Join)
自联接是表与自身的联接:
-- 查找员工及其经理
SELECT
e1.employee_id,
e1.first_name,
e1.last_name,
e2.first_name AS manager_first_name,
e2.last_name AS manager_last_name
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.employee_id;
-- 查找同一城市的客户
SELECT
c1.customer_id,
c1.first_name,
c1.last_name,
c2.customer_id AS related_customer_id,
c2.first_name AS related_first_name,
c2.last_name AS related_last_name
FROM customers c1
INNER JOIN customers c2 ON c1.city = c2.city
WHERE c1.customer_id < c2.customer_id;
8.5 交叉联接 (CROSS JOIN)
交叉联接返回两个表的笛卡尔积(所有可能的组合):
-- 产品与颜色的所有组合
SELECT
p.product_id,
p.product_name,
c.color_name
FROM products p
CROSS JOIN colors c;
9. 常用查询案例
9.1 销售数据分析
-- 按月统计销售额
SELECT
YEAR(order_date) AS year,
MONTH(order_date) AS month,
SUM(total) AS monthly_sales
FROM orders
GROUP BY YEAR(order_date), MONTH(order_date)
ORDER BY year, month;
-- 销售额最高的客户
SELECT
c.customer_id,
c.first_name,
c.last_name,
SUM(o.total) AS total_spent
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.first_name, c.last_name
ORDER BY total_spent DESC
LIMIT 10;
-- 产品销售排行
SELECT
p.product_id,
p.product_name,
SUM(oi.quantity) AS total_quantity,
SUM(oi.quantity * oi.price) AS total_revenue
FROM products p
INNER JOIN order_items oi ON p.product_id = oi.product_id
GROUP BY p.product_id, p.product_name
ORDER BY total_revenue DESC;
9.2 库存管理查询
-- 库存不足的产品
SELECT
product_id,
product_name,
stock,
reorder_level
FROM products
WHERE stock <= reorder_level
ORDER BY (reorder_level - stock) DESC;
-- 库存价值统计
SELECT
category_id,
SUM(stock * price) AS inventory_value
FROM products
GROUP BY category_id
ORDER BY inventory_value DESC;
-- 滞销产品分析
SELECT
p.product_id,
p.product_name,
p.stock,
COALESCE(SUM(oi.quantity), 0) AS total_sold
FROM products p
LEFT JOIN order_items oi ON p.product_id = oi.product_id
LEFT JOIN orders o ON oi.order_id = o.order_id AND o.order_date >= DATE_SUB(CURRENT_DATE, INTERVAL 3 MONTH)
GROUP BY p.product_id, p.product_name, p.stock
HAVING total_sold < 5 AND p.stock > 20
ORDER BY total_sold, p.stock DESC;
9.3 客户行为分析
-- 客户购买频率
SELECT
c.customer_id,
c.first_name,
c.last_name,
COUNT(o.order_id) AS order_count,
MIN(o.order_date) AS first_order,
MAX(o.order_date) AS last_order,
DATEDIFF(MAX(o.order_date), MIN(o.order_date)) / COUNT(o.order_id) AS avg_days_between_orders
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.first_name, c.last_name
HAVING COUNT(o.order_id) > 1
ORDER BY order_count DESC;
-- 客户留存分析
SELECT
YEAR(first_order) AS cohort_year,
MONTH(first_order) AS cohort_month,
COUNT(DISTINCT customer_id) AS total_customers,
COUNT(DISTINCT CASE WHEN months_since = 1 THEN customer_id END) AS retained_after_1_month,
COUNT(DISTINCT CASE WHEN months_since = 3 THEN customer_id END) AS retained_after_3_months,
COUNT(DISTINCT CASE WHEN months_since = 6 THEN customer_id END) AS retained_after_6_months,
COUNT(DISTINCT CASE WHEN months_since = 12 THEN customer_id END) AS retained_after_12_months
FROM (
SELECT
c.customer_id,
MIN(o1.order_date) AS first_order,
o2.order_date,
PERIOD_DIFF(
YEAR(o2.order_date) * 12 + MONTH(o2.order_date),
YEAR(MIN(o1.order_date)) * 12 + MONTH(MIN(o1.order_date))
) AS months_since
FROM customers c
INNER JOIN orders o1 ON c.customer_id = o1.customer_id
LEFT JOIN orders o2 ON c.customer_id = o2.customer_id AND o2.order_date > o1.order_date
GROUP BY c.customer_id, o2.order_date
) cohort_analysis
GROUP BY cohort_year, cohort_month
ORDER BY cohort_year, cohort_month;
10. 查询优化技巧
10.1 编写高效查询
提高查询性能的基本原则:
-- 1. 只查询需要的列,避免SELECT *
SELECT customer_id, first_name, last_name
FROM customers
WHERE country = 'USA';
-- 2. 使用索引列作为查询条件
SELECT *
FROM orders
WHERE customer_id = 123; -- 假设customer_id有索引
-- 3. 避免在WHERE子句中使用函数
-- 低效:
SELECT *
FROM orders
WHERE YEAR(order_date) = 2023;
-- 更好:
SELECT *
FROM orders
WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31';
-- 4. 尽量减少子查询,使用JOIN代替
-- 使用JOIN:
SELECT c.customer_id, c.first_name, COUNT(o.order_id) AS order_count
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.first_name;
10.2 EXPLAIN 分析
使用EXPLAIN命令分析查询执行计划:
-- 查看查询执行计划
EXPLAIN SELECT *
FROM customers
WHERE country = 'USA' AND city = 'New York';
-- 分析联接查询性能
EXPLAIN SELECT
c.first_name,
c.last_name,
o.order_id
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_date > '2023-01-01';
10.3 索引使用技巧
正确使用索引可大幅提高查询性能:
-- 为常用查询条件创建索引
CREATE INDEX idx_customers_country_city ON customers(country, city);
-- 复合索引的顺序很重要
-- 有效使用索引:
SELECT * FROM customers WHERE country = 'USA' AND city = 'New York';
-- 可能无法充分利用索引:
SELECT * FROM customers WHERE city = 'New York'; -- 如果索引是(country, city)
10.4 查询缓存与限制
利用LIMIT减少结果集大小:
-- 大数据集分页查询
SELECT *
FROM large_table
WHERE some_condition
ORDER BY id
LIMIT 100 OFFSET 10000;
-- 使用索引优化大偏移量查询
SELECT *
FROM large_table
WHERE id > 10000 AND some_condition
ORDER BY id
LIMIT 100;
10.5 通用查询优化建议
问题 | 解决方案 |
---|---|
查询速度慢 | 添加合适的索引,优化查询条件 |
联接操作慢 | 确保联接列有索引,减少联接表数量 |
排序慢 | 在排序列上建立索引,减少排序数据量 |
分组操作慢 | 在分组列上建立索引,预先过滤数据 |
子查询性能差 | 尝试重写为联接查询 |
全表扫描 | 添加适当的WHERE条件,建立索引 |
通过掌握MySQL的基础查询技巧,可以从数据库中高效地检索所需信息,满足业务需求。随着数据量和查询复杂度的增加,查询优化变得越来越重要。合理使用索引、编写高效的SQL语句、以及定期进行查询分析,都是提高数据库查询性能的关键步骤。
结语
感谢您的阅读!期待您的一键三连!欢迎指正!