MySQL 系列:第6篇 函数让数据更灵活
2026/6/16 10:18:58 网站建设 项目流程

IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。


上一篇文章我们学会了用 SELECT 精准取数,但直接取出的原始数据往往不够“好看”——日期格式需要调整、数值需要计算、字符串需要拼接。这时 MySQL 内置函数就派上了大用场。本篇聚焦常用函数、条件判断和分组聚合,让你的查询能力再上一个台阶。

1. 准备数据:建一张订单表

先用 Python 创建一张orders表,用于演示所有函数。

importmysql.connector conn=mysql.connector.connect(host="127.0.0.1",port=3306,user="root",password="MyNewPass123!",database="shop")cursor=conn.cursor()cursor.execute(""" CREATE TABLE IF NOT EXISTS orders(idINT AUTO_INCREMENT PRIMARY KEY, customer_name VARCHAR(50)NOT NULL, product VARCHAR(100)NOT NULL, amount DECIMAL(10,2)NOT NULL, quantity INT NOT NULL, order_date DATETIME NOT NULL, status VARCHAR(20)DEFAULT'pending')ENGINE=InnoDB""")cursor.execute("TRUNCATE orders")# 插入测试数据orders_data=[("张三","机械键盘",399.00,2,"2025-03-15 10:30:00","completed"),("李四","蓝牙耳机",259.00,1,"2025-04-20 14:00:00","completed"),("王五","USB-C 数据线",29.90,5,"2025-06-01 09:15:00","pending"),("赵六","显示器",1899.00,1,"2025-06-30 16:45:00","shipped"),("张三","鼠标垫",19.90,3,"2025-07-10 11:00:00","completed"),("李四","键盘手托",89.00,1,"2025-07-21 08:00:00","pending"),("孙八","网线",15.00,10,"2025-07-21 12:30:00","completed"),("钱七","移动硬盘",459.00,1,"2025-07-21 18:00:00","shipped"),]cursor.executemany("INSERT INTO orders (customer_name, product, amount, quantity, order_date, status) ""VALUES (%s, %s, %s, %s, %s, %s)", orders_data)conn.commit()print(f"✅ 插入了 {cursor.rowcount} 条订单")

预期输出

2. 字符串函数:文本处理利器

字符串函数在报表生成、数据清洗时极为常用。

Python 实战:格式化输出订单信息

cursor.execute(""" SELECT customer_name, CONCAT('¥', amount)AS 单价, CONCAT_WS(' x ', product, quantity)AS 商品信息, UPPER(status)AS 状态, CHAR_LENGTH(customer_name)AS 姓名字符数 FROM orders LIMIT5""")print(f"{'客户':<8} {'单价':<10} {'商品信息':<25} {'状态':<12} {'姓名长度'}")forrowincursor.fetchall(): print(f"{row[0]:<8} {row[1]:<10} {row[2]:<25} {row[3]:<12} {row[4]}")

预期输出

客户 单价 商品信息 状态 姓名长度 张三 ¥399.00 机械键盘 x2COMPLETED2李四 ¥259.00 蓝牙耳机 x1COMPLETED2王五 ¥29.90 USB-C 数据线 x5PENDING2赵六 ¥1899.00 显示器 x1SHIPPED2张三 ¥19.90 鼠标垫 x3COMPLETED2

注意LENGTH返回的是字节数,在utf8mb4字符集下,一个中文占 3-4 字节;CHAR_LENGTH返回的是字符数。统计中文字数一定要用后者。

3. 数值函数:计算就在数据库里完成

Python 实战:计算订单总价与折扣

cursor.execute(""" SELECT product, amount, quantity, amount * quantity AS 小计, ROUND(amount * quantity *0.9,2)AS 九折后, CEIL(amount * quantity *0.1)AS 优惠取整 FROM orders WHERE status='completed'""")print(f"{'商品':<15} {'单价':<8} {'数量':<6} {'小计':<10} {'九折后':<10} {'优惠取整'}")forrowincursor.fetchall(): print(f"{row[0]:<15} {row[1]:<8} {row[2]:<6} {row[3]:<10} {row[4]:<10} {row[5]}")

预期输出

商品 单价 数量 小计 九折后 优惠取整 机械键盘399.002798.00718.2080蓝牙耳机259.001259.00233.1026鼠标垫19.90359.7053.736网线15.0010150.00135.0015

4. 日期时间函数:轻松处理时间维度

Python 实战:订单时效分析

cursor.execute(""" SELECT customer_name, product, order_date, DATE_FORMAT(order_date,'%Y年%m月%d日 %H:%i')AS 格式化时间, DATEDIFF(NOW(), order_date)AS 距今已过天数, DATE_ADD(order_date, INTERVAL7DAY)AS 自动确认日 FROM orders WHERE status='pending'""")print(f"{'客户':<6} {'商品':<15} {'距今已过天数':<12} {'自动确认日'}")forrowincursor.fetchall(): print(f"{row[0]:<6} {row[1]:<15} {row[4]:<12} {row[5]}")

预期输出(根据当前日期略有不同):

客户 商品 距今已过天数 自动确认日 王五 USB-C 数据线502025-06-08 09:15:00 李四 键盘手托02025-07-28 08:00:00

常用格式化符

  • %Y四位年,%m月,%d

  • %H24小时制,%i分钟,%s

5. 条件判断函数:让 SQL 拥有逻辑

5.1 IF(expr, true_val, false_val)

三元运算符,简单判断就用它。

cursor.execute(""" SELECT product, quantity, IF(quantity>=5,'批发','零售')AS 订单类型 FROM orders""")forrowincursor.fetchall(): print(row)

预期输出

('机械键盘',2,'零售')('蓝牙耳机',1,'零售')('USB-C 数据线',5,'批发')...

5.2 CASE WHEN:多条件分支

CASE WHEN是 SQL 中的if-elif-else,非常强大。

cursor.execute(""" SELECT product, amount * quantity AS total, CASE WHEN amount * quantity>=500THEN'大额订单'WHEN amount * quantity>=100THEN'中等订单'ELSE'小额订单'END AS 订单级别 FROM orders""")print(f"{'商品':<15} {'总价':<10} {'级别'}")forrowincursor.fetchall(): print(f"{row[0]:<15} {row[1]:<10} {row[2]}")

预期输出

商品 总价 级别 机械键盘798.00大额订单 蓝牙耳机259.00中等订单 USB-C 数据线149.50中等订单 显示器1899.00大额订单 鼠标垫59.70小额订单...

6. 聚合函数与 GROUP BY:数据分析的基石

前面几篇都是逐行操作,而聚合函数 + GROUP BY能将数据“折叠”成汇总信息,是报表、统计的核心。

6.1 常用聚合函数

6.2 GROUP BY 分组统计

cursor.execute(""" SELECT customer_name, COUNT(*)AS 订单数, SUM(amount * quantity)AS 总消费, AVG(amount)AS 平均单价, MAX(order_date)AS 最近下单时间 FROM orders GROUP BY customer_name ORDER BY 总消费 DESC""")print(f"{'客户':<6} {'订单数':<6} {'总消费':<10} {'平均单价':<10} {'最近下单时间'}")forrowincursor.fetchall(): print(f"{row[0]:<6} {row[1]:<6} {row[2]:<10} {row[3]:<10} {row[4]}")

预期输出

客户 订单数 总消费 平均单价 最近下单时间 赵六11899.001899.0000002025-06-3016:45:00 张三2857.70209.4500002025-07-1011:00:00...

GROUP BY 的执行逻辑:先将数据按指定列分成若干组,然后对每组分别计算聚合函数。

6.3 HAVING:分组后的过滤

WHERE在分组过滤行,HAVING在分组过滤组。

# 找出总消费超过 500 的客户cursor.execute(""" SELECT customer_name, SUM(amount * quantity)AS total_spent FROM orders GROUP BY customer_name HAVING total_spent>500ORDER BY total_spent DESC""")forrowincursor.fetchall(): print(f"{row[0]}: ¥{row[1]}")

预期输出

赵六: ¥1899.00 张三: ¥857.70 钱七: ¥459.00 ← 这条不会出现,因为<500

6.4 常见误区:SELECT 中放非聚合列

下面的 SQL 会报错(或返回不确定的结果,取决于sql_mode):

-- 错误示例 SELECT customer_name, product, SUM(amount)FROM orders GROUP BY customer_name;

product没有出现在GROUP BY中,也没有用聚合函数包裹——MySQL 不知道取组内的哪一条值。MySQL 8.0 默认ONLY_FULL_GROUP_BY会直接拒绝此查询。

最佳实践:SELECT 中的列要么在 GROUP BY 中,要么被聚合函数包裹。

7. 综合实战:用 Python 生成销售日报

将以上所有知识串联,写一个生成每日销售报告的函数:

def daily_sales_report(date_str=None):""" 生成某天的销售报告 date_str:'2025-07-21'或 None(今天)"""ifdate_str is None: from datetimeimportdatedate_str=date.today().isoformat()conn=mysql.connector.connect(host="127.0.0.1",port=3306,user="root",password="MyNewPass123!",database="shop")cursor=conn.cursor(dictionary=True)# 当日总览cursor.execute(""" SELECT COUNT(*)AS order_count, SUM(amount * quantity)AS total_revenue, ROUND(AVG(amount * quantity),2)AS avg_order_value FROM orders WHERE DATE(order_date)=%s""",(date_str,))overview=cursor.fetchone()# 分类统计cursor.execute(""" SELECT status, COUNT(*)AS cnt, SUM(amount * quantity)AS revenue FROM orders WHERE DATE(order_date)=%s GROUP BY status""",(date_str,))by_status=cursor.fetchall()cursor.close()conn.close()returnoverview, by_status# 使用overview, by_status=daily_sales_report("2025-07-21")print("📊 2025-07-21 销售日报")print(f"订单数: {overview['order_count']} | "f"总营收: ¥{overview['total_revenue']} | "f"客单价: ¥{overview['avg_order_value']}")print("按状态分布:")forrowinby_status: print(f" {row['status']}: {row['cnt']}单, ¥{row['revenue']}")

预期输出

📊2025-07-21 销售日报 订单数:3|总营收: ¥563.00|客单价: ¥187.67 按状态分布: completed:1单, ¥150.00 pending:1单, ¥89.00 shipped:1单, ¥459.00

8. 动手试试:分析订单数据

基于我们插入的 8 条订单,完成以下挑战:

  1. 统计每个状态(status)的订单数,并用CASE WHEN在结果中把completed显示为“已完成”,pending显示为“待处理”,shipped显示为“已发货”。

  2. 计算每种商品的总销量(quantity 之和),并按销量降序排列。

  3. 找出下单最早的 3 个订单,只显示客户名和格式化后的日期(如“2025年03月15日”)。

  4. 查询今天(CURDATE())的订单,如果没有结果,解释为什么。

提示:问题 4 中,数据是静态插入的,而CURDATE()返回真实当前日期。如果今天不是数据中任意一条的order_date,结果自然为空。试试插入一条今天的订单再查。

9. 总结

本篇我们完整覆盖了 MySQL 常用四大类函数及分组聚合:

  • 字符串函数:拼接(CONCAT)、截取(SUBSTRING)、长度(CHAR_LENGTH)

  • 数值函数:四舍五入(ROUND)、取整(CEIL/FLOOR)

  • 日期函数:格式化(DATE_FORMAT)、运算(DATE_ADD/DATEDIFF)

  • 条件函数:IF 简单判断、CASE WHEN 多分支

  • 聚合 + GROUP BY:统计核心,HAVING 过滤分组

掌握这些后,你已经能用 SQL 完成 80% 的数据处理需求。下一篇我们将解锁多表连接(JOIN),让数据跨表关联,实现更复杂的查询。下次见!

想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询