当前位置: 首页 > news >正文

MySQL 系列:第5篇 从一张表中精准取数

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


前面几篇我们建好了表、插入了数据。今天终于要回答那个核心问题:怎么把数据高效、精准地取出来?SELECT 是 SQL 中使用最频繁的语句,掌握它就意味着你能随心所欲地“问”数据库任何问题。本篇用 Python 配合实战,彻底拿下单表查询。

1. 准备数据:搭建测试环境

先用 Python 创建一个employees表并灌入足够多的测试数据,供后续所有查询使用。

importmysql.connectorimportrandomimportstring 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 employees(idINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50)NOT NULL, department VARCHAR(30), salary DECIMAL(10,2), age TINYINT UNSIGNED, city VARCHAR(20), hire_date DATE, is_active TINYINT DEFAULT1)ENGINE=InnoDB""")# 清空旧数据cursor.execute("TRUNCATE employees")# 批量插入 20 条测试数据departments=["技术部","产品部","市场部","财务部","人事部"]cities=["北京","上海","深圳","杭州","成都"]names=["张三","李四","王五","赵六","钱七","孙八","周九","吴十","郑一","冯二","陈三","褚四","卫五","蒋六","沈七","韩八","杨九","朱十","秦一","尤二"]data=[]foriinrange(20): name=names[i]dept=random.choice(departments)salary=round(random.uniform(8000,30000),2)age=random.randint(22,55)city=random.choice(cities)hire_date=f"202{random.randint(0,5)}-{random.randint(1,12):02d}-{random.randint(1,28):02d}"data.append((name,dept,salary,age,city,hire_date))cursor.executemany("INSERT INTO employees (name, department, salary, age, city, hire_date) VALUES (%s,%s,%s,%s,%s,%s)", data)conn.commit()print(f"✅ 插入了 {cursor.rowcount} 条测试数据")

预期输出

2. SELECT 基础:想看什么,就写什么

2.1 最简单的查询

# 查看所有列cursor.execute("SELECT * FROM employees")rows=cursor.fetchall()print(f"共 {len(rows)} 条记录\n")# 只看特定列cursor.execute("SELECT name, department, salary FROM employees")forrowincursor.fetchmany(3):# 只取前 3 行print(row)

预期输出

20条记录('张三','技术部', Decimal('18500.00'))('李四','市场部', Decimal('9200.00'))('王五','财务部', Decimal('27300.00'))

SELECT *的陷阱:生产环境中尽量避免*,因为:

  • 返回了不需要的列,浪费网络带宽

  • 表结构变更后,列顺序可能变化,导致代码出 bug

  • 明确列出列名,代码可读性更高

2.2 列别名(AS)

让结果更易读,同时可在 Python 中通过列名取值:

cursor.execute(""" SELECT name AS 姓名, department AS 部门, salary AS 月薪 FROM employees""")forrowincursor.fetchmany(3): print(f"{row[0]} | {row[1]} | {row[2]}")

3. WHERE:条件筛选的艺术

WHERE 是 SELECT 的灵魂——不加条件就是全表扫描,加了条件才能精准命中。

3.1 比较运算符

# 查询月薪大于 20000 的员工cursor.execute("SELECT name, salary FROM employees WHERE salary > 20000")print("高薪员工:")forrowincursor.fetchall(): print(f" {row[0]} - ¥{row[1]}")

3.2 逻辑运算符:AND、OR、NOT

# 技术部且月薪大于 15000cursor.execute(""" SELECT name, department, salary FROM employees WHERE department='技术部'AND salary>15000""")print("技术部高薪:", cursor.fetchall())# 北京或上海的员工cursor.execute(""" SELECT name, city FROM employees WHERE city='北京'OR city='上海'""")print("京沪员工:", cursor.fetchall())# 不是技术部的员工cursor.execute(""" SELECT name, department FROM employees WHERE NOT department='技术部'""")

3.3 BETWEEN:区间查询

# 月薪在 10000 ~ 20000 之间cursor.execute(""" SELECT name, salary FROM employees WHERE salary BETWEEN10000AND20000""")print("中等薪资:", cursor.fetchall())# 等同于 WHERE salary >= 10000 AND salary <= 20000

3.4 IN:列举匹配

# 查询指定部门的员工cursor.execute(""" SELECT name, department FROM employees WHERE department IN('技术部','产品部')""")print("技术与产品:", cursor.fetchall())

IN比多个OR更简洁,而且 MySQL 会对其内部优化,将列表转为二分查找。

3.5 LIKE:模糊查询

  • %匹配任意多个字符

  • _匹配单个字符

# 查询姓“张”的员工cursor.execute("SELECT name FROM employees WHERE name LIKE '张%'")print("张姓员工:", cursor.fetchall())# 查询名字第二个字是“十”的cursor.execute("SELECT name FROM employees WHERE name LIKE '_十%'")print("第二个字是十:", cursor.fetchall())

性能警告LIKE '%张'这种前缀模糊会导致索引失效,因为 B+Tree 索引只能从左向右匹配。后续索引章节会详解。

3.6 IS NULL / IS NOT NULL

# 查询没有填写年龄的员工(假设允许 NULL)cursor.execute("SELECT name, age FROM employees WHERE age IS NULL")

注意:不能用= NULL,必须用IS NULL

4. DISTINCT:去重

想知道公司员工分布在哪些城市?

cursor.execute("SELECT DISTINCT city FROM employees")print("城市分布:",[row[0]forrowincursor.fetchall()])# 多列去重cursor.execute("SELECT DISTINCT department, city FROM employees")print("部门-城市组合:", cursor.fetchall())

5. ORDER BY:排序

# 按薪资降序,薪资相同则按年龄升序cursor.execute(""" SELECT name, salary, age FROM employees ORDER BY salary DESC, age ASC""")print("薪资排行榜:")forrowincursor.fetchall(): print(f" {row[0]} | ¥{row[1]} | {row[2]}岁")
  • ASC升序(默认),DESC降序

  • 多列排序:先按第一列排,相同时按第二列排

6. LIMIT 与 OFFSET:分页查询

这是构建任何列表页面都少不了的技能。

6.1 基础分页

# 每页 5 条,取第 1 页page=1page_size=5offset=(page -1)* page_size cursor.execute(""" SELECT id, name, salary FROM employees ORDER BYidLIMIT %s OFFSET %s""",(page_size, offset))print(f"第 {page} 页:")forrowincursor.fetchall(): print(f" #{row[0]} {row[1]} ¥{row[2]}")

6.2 快捷写法

LIMIT offset, count是另一种写法,但不推荐,因为可读性差:

-- 这两种等价 LIMIT5OFFSET10LIMIT10,5-- 不推荐:与 LIMIT count OFFSET offset 顺序相反

6.3 分页深翻的性能问题

OFFSET很大时(比如第 10000 页),MySQL 仍然需要扫描前面所有的行再丢弃,效率极低。解决方案后续优化篇会讲“延迟关联”等技巧。

7. 实战小工具:用 Python 封装一个灵活的查询函数

将今天学的知识点封装成可复用的工具函数:

def query_employees(filters=None,sort_by="id",sort_order="ASC",page=1,page_size=10):""" 灵活的 employee 查询器 filters: dict,如{"department":"技术部","salary_min":10000}""" conn=mysql.connector.connect(host="127.0.0.1",port=3306,user="root",password="MyNewPass123!",database="shop")cursor=conn.cursor(dictionary=True)# 返回字典格式where_clauses=["1=1"]# 占位,方便动态拼接params=[]iffilters:if"department"infilters: where_clauses.append("department = %s")params.append(filters["department"])if"city"infilters: where_clauses.append("city = %s")params.append(filters["city"])if"salary_min"infilters: where_clauses.append("salary >= %s")params.append(filters["salary_min"])if"salary_max"infilters: where_clauses.append("salary <= %s")params.append(filters["salary_max"])if"name_like"infilters: where_clauses.append("name LIKE %s")params.append(f"%{filters['name_like']}%")where_sql=" AND ".join(where_clauses)offset=(page -1)* page_size sql=f""" SELECT id, name, department, salary, city FROM employees WHERE{where_sql}ORDER BY{sort_by}{sort_order}LIMIT %s OFFSET %s""" params.extend([page_size, offset])cursor.execute(sql, params)results=cursor.fetchall()# 查询总记录数cursor.execute(f"SELECT COUNT(*) AS total FROM employees WHERE {where_sql}", params[:-2])total=cursor.fetchone()["total"]cursor.close()conn.close()returnresults, total# 使用示例results, total=query_employees(filters={"department":"技术部","salary_min":15000},sort_by="salary",sort_order="DESC")print(f"共 {total} 条,本页 {len(results)} 条")forrinresults: print(f" {r['name']} | {r['department']} | ¥{r['salary']}")

8. 动手试试:探索员工数据

基于我们插入的 20 条数据,请完成以下挑战:

  1. 查询 30 岁以下且月薪超过 15000 的员工,按薪资降序排列,只显示前 3 条。

  2. 查询在北京或深圳的市场部员工

  3. 查询名字包含“十”字的所有员工,去重后按姓名排序。

  4. 实现分页功能:每页 4 条,打印第 2 页的数据,同时打印总页数。

参考答案可在控制台中逐一验证,预期能匹配到正确的结果集。

动手提示:将以上查询翻译为 Python 代码执行,观察fetchall()返回的数据是否与你的预期一致。如不一致,仔细检查 WHERE 条件的逻辑组合。

9. 总结

本篇我们完整掌握了 SELECT 单表查询的核心语句:

  • SELECT 列名代替*,明确意图

  • WHERE配合=>AND/ORBETWEENINLIKE精准筛选

  • DISTINCT去重

  • ORDER BY排序

  • LIMIT + OFFSET实现分页

这些是 SQL 的最高频操作,也是后续 JOIN、子查询、窗口函数等高级话题的基石。下一篇我们将解锁函数与分组聚合,用 SQL 直接做数据分析。下次见!

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

http://www.cnnetsun.cn/news/2943640.html

相关文章:

  • 影刀RPA进阶教程_子流程设计的6条黄金法则从地狱面条到清晰架构
  • FOCAS2开发指南:连接FANUC数控系统实现数据采集与监控
  • 2026年度软件研发效能前瞻:智能编码工具的多维测评与极致产出指南
  • macOS开源组件仓库:系统开发者必备的官方参考实现
  • Edge浏览器如何零代码接入Gemini 3.1 Pro提升办公效率
  • RK3588无人机主控实战:异构计算、AI推理与系统集成全解析
  • 红米10X 5G刷机全攻略:从解锁Bootloader到刷入第三方ROM
  • 基于OV2640传感器实现工业级全局快门效果的软硬件方案
  • 城通网盘高速下载终极指南:免费开源工具ctfileGet完全解析
  • 时序回归实战:从CSV到上线预测的Python全流程
  • Gemini原生生成Office文档:打破复制粘贴的交互范式
  • 图片去水印用什么工具?2026电脑手机免费去水印软件排行
  • Hermes Agent开源框架深度解析:本地化、可追溯、可沉淀的AI工作流架构
  • Codex CLI:轻量级本地AI编码协作者,支持OpenAI/DeepSeek多模型
  • Seaborn配色决策手册:按数据类型选Palette
  • 安阳高口碑黄金铂金回收白银回收实体老店排行 5 家靠谱门店电话地址全收录
  • 139.时间嵌入+残差UNet|DDPM噪声预测网络核心架构解析
  • 独热编码原理与工程实践:分类变量特征工程全解析
  • 还在为视频笔记发愁?Bili2text免费神器3分钟搞定B站视频转文字终极指南
  • 干货分享:图解两种常见回溯解法(一)
  • 当你的 Jira 成为 AI 训练数据:深度解析 Atlassian 智能意图与隐私边界
  • 【计算机毕业设计案例】基于 SpringBoot 框架的钱币文化交流平台设计与实践 钱币收藏资讯分享与互动交流系统(程序+文档+讲解+定制)
  • Pandas reset_index() 原理与生产级避坑指南
  • 植物大战僵尸终极修改器:PvZ Tools完整使用指南
  • Claude Code 从 Demo 到产线 · 企业 Harness 工程化的 8 道关卡
  • 从软件学习到OJ实战:构建高效算法能力提升路径
  • 5分钟上线可计费AI模型服务:Replicate+Cog+Stripe实战指南
  • 程序员就业:2026 年还能靠什么拿到 offer:别只背概念,先跑通这个闭环
  • MPC866 PowerQUICC:嵌入式RISC核心的架构解析与微架构设计
  • 一套键鼠控制多台电脑:Input Leap跨平台KVM终极指南