尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

MySQL SQL入门:从安装到多表查询的实战指南

MySQL SQL入门:从安装到多表查询的实战指南 1. 从零开始为什么今天你依然需要学习SQL如果你刚接触编程或者数据分析可能会觉得SQLStructured Query Language是一门“古老”的语言。毕竟它诞生于上世纪70年代比很多人的父母年纪都大。但恰恰是这门“古老”的语言在今天的数据驱动时代其重要性不降反升几乎成了所有与数据打交道岗位的“硬通货”。无论是想成为一名后端工程师、数据分析师、产品经理还是从事运维、测试甚至是金融、市场等非技术岗位SQL都是你绕不开的一道坎。为什么因为数据是现代商业的血液而SQL就是抽取、过滤、分析这些血液最直接、最高效的工具。你可以用Python、Java写复杂的业务逻辑但当你需要从数据库中快速找出“上个月销售额最高的10个产品”或者“最近一周活跃但未下单的用户”时最优雅、最高效的方式往往就是写一条SQL语句。它直接与数据库“对话”省去了在应用层进行繁琐循环和过滤的步骤。很多公司面试从互联网大厂到初创企业SQL都是必考项。那些热搜词如“sql面试题”、“慢sql优化”、“sql优化”就是最好的证明——企业不仅要求你会写还要求你写得好、写得快。而MySQL作为世界上最流行的开源关系型数据库之一是我们学习SQL的最佳“训练场”。它免费、轻量、社区活跃从个人博客到大型网站都在使用。因此这个系列文章我们就以MySQL为环境手把手带你从完全不懂到能够熟练使用SQL进行数据操作。别担心它听起来很技术我会用最直白的话把核心概念和实操步骤讲清楚。我们的目标不是成为数据库专家而是掌握那20%最常用、能解决80%问题的SQL技能。2. 环境准备安装MySQL与第一个“Hello World”在真正写SQL之前我们得先有个“舞台”。对于初学者我强烈推荐使用集成安装包它能帮你省去大量配置的麻烦避免踩到“mysql安装启动服务报错”这类坑。2.1 选择并安装MySQL目前MySQL有两个主要分支Oracle官方的MySQL和完全开源兼容的MariaDB。对于学习而言两者几乎没区别。我建议新手直接安装MySQL Installer for Windows如果你用Windows或者使用HomebrewmacOS和apt/yumLinux来安装。以Windows为例详细步骤如下访问官网别去乱七八糟的下载站直接搜索“mysql download”进入Oracle官网。找到“MySQL Community (GPL) Downloads”部分选择“MySQL Installer for Windows”。你会发现有mysql-installer-web-community和mysql-installer-community两个版本前者是在线安装包较小后者是离线安装包较大。网络稳定的话下载在线安装包即可。运行安装程序下载后运行安装类型选择“Developer Default”它会安装MySQL服务器、客户端工具如MySQL Workbench和一些其他组件非常适合学习和开发。产品配置这是关键步骤。在配置类型Config Type页面选择“Development Computer”。在账户和角色Accounts and Roles页面为MySQL的root用户设置一个强密码务必记住它我见过太多人在本地环境用“123456”这是个坏习惯。你可以添加一个日常使用的普通用户比如dev_user并赋予它适当的权限。Windows服务确保将MySQL服务器配置为Windows服务并设置开机自启动。这样以后你就可以在服务列表里启动/停止MySQL而不需要每次手动敲命令。完成安装一路点击“Execute”执行安装和配置。完成后你可以在开始菜单找到“MySQL 8.0 Command Line Client”或“MySQL Workbench”。注意安装过程中如果遇到类似“microsoft.vclibs.140”之类的错误通常是因为系统缺少Visual C运行库。去微软官网下载并安装“Microsoft Visual C Redistributable for Visual Studio”的最新版本即可解决。这是Windows软件安装的常见依赖问题。验证安装是否成功打开命令提示符CMD或PowerShell输入以下命令mysql -u root -p回车后输入你刚才设置的root密码。如果成功进入你会看到MySQL的命令行提示符mysql。恭喜你的数据库舞台已经搭好了输入exit;可以退出。2.2 认识我们的工具命令行 vs. 图形化界面刚进入黑乎乎的命令行你可能会有点发怵。别担心我们有两种选择MySQL命令行客户端最直接、最轻量的工具。所有操作通过输入SQL命令完成。它的好处是纯粹、快速能让你更专注于SQL语句本身适合学习和执行简单操作。缺点是不直观尤其是查看数据时。MySQL Workbench官方提供的图形化界面GUI工具。它提供了可视化的数据库管理、SQL编辑、数据建模就是生成“mysql的表导出er关系图”、服务器状态监控等功能。对于初学者我强烈推荐从Workbench开始。它的自动补全、语法高亮、结果集表格化展示能极大提升学习效率和体验。打开MySQL Workbench它会自动检测本地的MySQL实例。点击连接输入密码你就进入了一个可视化的操作环境。主界面中央的查询编辑器Query Editor窗口就是我们将要大展拳脚的地方。第一个“Hello World”创建数据库在SQL的世界里我们操作的数据都存放在“数据库”Database中。一个数据库就像一个大仓库里面有很多货架表。我们的第一步就是创建自己的仓库。 在Workbench的查询编辑器里输入以下命令CREATE DATABASE my_first_db;点击工具栏的闪电图标执行当前语句或者按CtrlEnter。下方输出窗口会显示“Query OK, 1 row affected”。这表示执行成功。接着我们要告诉MySQL后续的操作都在这个新仓库里进行USE my_first_db;执行后你就“进入”了这个数据库。现在我们的舞台已经清空准备迎接第一个演员——表Table。3. SQL语言基石理解DDL、DML与DQL在深入写代码之前我们必须先理解SQL语言的三大核心组成部分DDL、DML和DQL。这是所有SQL操作的基石理解了它们你就能对任何SQL语句进行归类并明白其意图。3.1 DDL定义数据库的“建筑师”DDL全称Data Definition Language即数据定义语言。顾名思义它的工作是定义创建、修改、删除数据库本身的结构而不是里面的数据。DDL操作的对象是数据库、表、视图、索引等“容器”和“框架”。执行DDL语句通常是一个“重量级”操作因为它直接改变结构在很多数据库里会隐式提交事务并可能锁表。核心DDL命令有三个CREATE创建。我们刚才用的CREATE DATABASE就是典型的DDL。ALTER修改。当需要给已存在的表增加一个字段、修改字段类型或重命名表时就用它。DROP删除。删除数据库、表、索引等。这个命令要慎用DROP TABLE会直接把整张表和里面的数据全部抹掉且通常无法恢复。一个完整的DDL实战创建用户表假设我们要为一个小型网站创建一张用户表它需要记录用户ID、用户名、邮箱和注册时间。CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, email VARCHAR(100) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );让我们拆解这条语句CREATE TABLE users创建一张名为users的表。id INT PRIMARY KEY AUTO_INCREMENT定义一个整数类型的id字段。PRIMARY KEY表示它是主键唯一标识每一行。AUTO_INCREMENT是MySQL的特性表示这个值会自动增长我们插入数据时不用管它。username VARCHAR(50) NOT NULL UNIQUE定义一个可变长度字符串最多50字符的username字段。NOT NULL表示该字段不能为空UNIQUE表示用户名不能重复。email VARCHAR(100) NOT NULL定义邮箱字段非空。created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP定义注册时间字段类型是时间戳。DEFAULT CURRENT_TIMESTAMP是一个非常有用的设置它表示如果不显式提供这个字段的值数据库会自动用当前时间填充。执行这条语句后一个结构清晰的users表就诞生了。你可以用DESC users;命令查看它的结构。3.2 DML打理数据库内容的“园丁”DML全称Data Manipulation Language即数据操纵语言。它的工作是对表里的具体数据进行增、删、改。这是日常业务开发中最频繁使用的部分。DML操作通常是在事务内的你可以提交Commit使更改永久化也可以回滚Rollback撤销更改。核心DML命令有四个INSERT插入新数据。UPDATE更新已有数据。DELETE删除数据注意是删除表中的行不是删除表本身。SELECT ... INTO一种特殊的插入将查询结果插入新表。让我们往users表里添点数据-- 插入一条完整数据 INSERT INTO users (username, email) VALUES (zhangsan, zhangsanexample.com); -- 插入多条数据效率更高 INSERT INTO users (username, email) VALUES (lisi, lisiexample.com), (wangwu, wangwuexample.com), (zhaoliu, zhaoliuexample.com);注意我们没有为id和created_at字段提供值。id会因为AUTO_INCREMENT自动生成1,2,3,4created_at会因为DEFAULT CURRENT_TIMESTAMP自动记录插入的时间。这就是DDL定义好的结构在起作用。再来试试更新和删除-- 更新将用户zhangsan的邮箱改为新的 UPDATE users SET email zhangsan_newexample.com WHERE username zhangsan; -- 删除删除用户名为zhaoliu的记录 DELETE FROM users WHERE username zhaoliu;这里有一个至关重要的知识点WHERE子句。在UPDATE和DELETE操作中几乎任何时候都要加上WHERE条件除非你确实想更新或删除整张表的所有数据。忘记加WHERE是初级开发者最容易酿成的“悲剧”之一可能导致全表数据被误改或清空。所以在执行UPDATE/DELETE前养成先用SELECT带上相同WHERE条件确认目标数据的习惯。3.3 DQL从数据库中提取信息的“侦探”DQL全称Data Query Language即数据查询语言。虽然理论上它属于DML的一部分但由于其极端重要性和使用频率人们通常将其单独归类。它的核心命令只有一个SELECT。但就是这个SELECT构成了SQL语言最强大、最灵活的部分。数据分析、报表生成、业务逻辑支撑都离不开复杂的查询。SELECT的基本骨架是SELECT column1, column2, ... FROM table_name WHERE conditions ORDER BY column_name [ASC|DESC] LIMIT number;SELECT指定要查询哪些列。*代表所有列。FROM指定从哪张或哪些表查询。WHERE设置过滤条件只返回满足条件的行。ORDER BY对结果进行排序。ASC升序默认DESC降序。LIMIT限制返回的行数常用于分页。来几个查询例子-- 1. 查询所有用户的所有信息 SELECT * FROM users; -- 2. 只查询用户名和邮箱 SELECT username, email FROM users; -- 3. 查询所有邮箱以example.com结尾的用户并按注册时间倒序排列 SELECT username, email, created_at FROM users WHERE email LIKE %example.com ORDER BY created_at DESC; -- 4. 只查询最早注册的那个用户 SELECT * FROM users ORDER BY created_at ASC LIMIT 1;LIKE是用于模糊匹配的操作符%代表任意多个字符。WHERE email LIKE %example.com就能匹配所有example.com域名的邮箱。4. 深入SELECT查询条件、函数与初步连接掌握了SELECT的基础形式我们来看看如何让它变得更强大。查询的精髓在于精确地定位和塑造你需要的数据。4.1 丰富的WHERE条件不只是等于WHERE子句可以使用多种操作符来构建复杂的过滤条件比较操作符,或!不等于,,,。-- 查询id大于2的用户 SELECT * FROM users WHERE id 2;逻辑操作符AND,OR,NOT。用于组合多个条件。-- 查询用户名是lisi或者wangwu的用户 SELECT * FROM users WHERE username lisi OR username wangwu; -- 查询id在1到3之间且包含1和3的用户 SELECT * FROM users WHERE id 1 AND id 3; -- 更优雅的方式是使用 BETWEEN SELECT * FROM users WHERE id BETWEEN 1 AND 3;IN 操作符检查某个值是否在一个列表里。-- 等价于上面的 OR 语句但更简洁 SELECT * FROM users WHERE username IN (lisi, wangwu);LIKE 与 通配符%匹配任意字符序列包括零个字符_匹配单个字符。-- 查询用户名以zhang开头的用户 SELECT * FROM users WHERE username LIKE zhang%; -- 查询用户名第二个字符是a的用户 SELECT * FROM users WHERE username LIKE _a%;IS NULL / IS NOT NULL判断字段是否为空值NULL。注意不能用 NULL来判断-- 假设我们有一个可选的phone字段查询未填写电话的用户 SELECT * FROM users WHERE phone IS NULL;4.2 使用函数处理数据让查询结果更友好SQL内置了大量函数可以在查询时对数据进行计算、转换和聚合。文本函数-- UPPER, LOWER 转换大小写 SELECT username, UPPER(email) AS upper_email FROM users; -- CONCAT 连接字符串 SELECT CONCAT(username, (, email, )) AS user_info FROM users; -- SUBSTRING 截取字符串 SELECT SUBSTRING(email, 1, 5) AS email_prefix FROM users;时间日期函数-- NOW() 获取当前时间 SELECT NOW(); -- DATE_FORMAT 格式化时间输出 SELECT username, DATE_FORMAT(created_at, %Y-%m-%d) AS reg_date FROM users; -- DATEDIFF 计算日期差 SELECT username, DATEDIFF(NOW(), created_at) AS days_since_reg FROM users;聚合函数这是数据分析的核心。它们对一组值执行计算并返回单个值。常与GROUP BY子句一起使用。COUNT()计数。-- 计算总用户数 SELECT COUNT(*) AS total_users FROM users; -- 计算邮箱不为空的用户数 SELECT COUNT(email) AS users_with_email FROM users;SUM()求和。AVG()求平均值。MAX()/MIN()求最大/最小值。假设我们有一张订单表ordersorder_id,user_id,amount,order_date我们可以做如下分析-- 计算总销售额、平均订单金额、最大订单金额 SELECT SUM(amount) AS total_sales, AVG(amount) AS avg_order_value, MAX(amount) AS max_order_value FROM orders; -- 按用户分组计算每个用户的总订单金额和订单数 SELECT user_id, COUNT(*) AS order_count, SUM(amount) AS total_spent FROM orders GROUP BY user_id ORDER BY total_spent DESC; -- 按消费总额降序排列GROUP BY子句是关键它告诉数据库按哪个字段进行分组聚合。SELECT后面跟着的要么是GROUP BY的字段要么是聚合函数。4.3 表的连接JOIN关联数据的魔法现实中的数据很少只存在于一张表里。用户信息在users表订单信息在orders表。我们经常需要把相关联的数据从多张表里一起查出来这就需要用到JOIN。为了演示我们再创建一张orders表CREATE TABLE orders ( order_id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, amount DECIMAL(10, 2) NOT NULL, -- 金额10位数字其中2位小数 order_date DATE, FOREIGN KEY (user_id) REFERENCES users(id) -- 外键关联users表的id ); INSERT INTO orders (user_id, amount, order_date) VALUES (1, 99.99, 2023-10-01), (2, 199.50, 2023-10-02), (1, 50.00, 2023-10-03), (3, 299.00, 2023-10-04);最常用的JOIN是INNER JOIN内连接它只返回两个表中连接字段匹配的行。-- 查询所有订单并显示下单用户的用户名 SELECT o.order_id, u.username, o.amount, o.order_date FROM orders o -- 给orders表起一个别名o方便书写 INNER JOIN users u ON o.user_id u.id; -- 通过user_id和id关联这条语句的逻辑是从orders表别名o出发去users表别名u里找连接条件是o.user_id等于u.id。结果会包含orders表里所有有对应用户的订单而users表里没有订单的用户比如我们之前删除的zhaoliu如果他有id的话则不会出现在结果中。除了INNER JOIN还有LEFT JOIN左连接返回左表orders的所有行即使右表users中没有匹配。如果右表无匹配则结果中右表的部分为NULL。-- 假设有一个订单的user_id在users表里不存在比如5 INSERT INTO orders (user_id, amount, order_date) VALUES (5, 10.00, 2023-10-05); -- 使用LEFT JOIN这条订单依然会出现但username为NULL SELECT o.order_id, u.username, o.amount FROM orders o LEFT JOIN users u ON o.user_id u.id;RIGHT JOIN右连接与LEFT JOIN相反返回右表的所有行。FULL OUTER JOIN全外连接返回两个表中所有的行不匹配的部分用NULL填充。MySQL不直接支持FULL JOIN但可以通过UNION LEFT JOIN和RIGHT JOIN实现。理解JOIN是SQL学习中的一个重要里程碑它让你能从分散的表中整合出有业务意义的信息视图。5. 实战演练与避坑指南从建库到复杂查询现在让我们把所有知识串联起来完成一个模拟的小型电商数据查询任务。同时我会分享一些新手最容易踩的坑和优化小技巧。5.1 综合案例电商数据查询场景我们管理一个简单的电商数据库有users用户、products产品、orders订单、order_items订单明细四张表。现在需要生成一份报告“2023年10月消费金额最高的前5名用户及其购买最多的商品名称”。步骤1理解表结构并插入模拟数据为了节省篇幅这里只给出关键字段你可以自行补充其他字段如价格、分类等-- 1. 产品表 CREATE TABLE products ( product_id INT PRIMARY KEY AUTO_INCREMENT, product_name VARCHAR(100) NOT NULL ); INSERT INTO products (product_name) VALUES (手机), (笔记本电脑), (耳机), (鼠标); -- 2. 订单明细表连接订单和产品因为一个订单可能包含多个商品 CREATE TABLE order_items ( item_id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, product_id INT NOT NULL, quantity INT NOT NULL, price DECIMAL(10, 2) NOT NULL, FOREIGN KEY (order_id) REFERENCES orders(order_id), FOREIGN KEY (product_id) REFERENCES products(product_id) ); -- 为之前创建的4个订单添加明细 INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (1, 1, 1, 99.99), -- 订单1买了1个手机 (2, 2, 1, 199.50), -- 订单2买了1个笔记本 (3, 3, 2, 25.00), -- 订单3买了2个耳机单价25 (4, 1, 1, 299.00); -- 订单4买了1个手机假设是高端款步骤2拆解问题分步查询这是一个相对复杂的问题我们可以分两步走第一步找出2023年10月消费金额最高的前5名用户。消费金额需要从order_items表计算quantity * price并关联到orders表得到用户和日期再关联到users表得到用户名。SELECT u.user_id, u.username, SUM(oi.quantity * oi.price) AS total_spent FROM users u INNER JOIN orders o ON u.id o.user_id INNER JOIN order_items oi ON o.order_id oi.order_id WHERE o.order_date BETWEEN 2023-10-01 AND 2023-10-31 GROUP BY u.user_id, u.username ORDER BY total_spent DESC LIMIT 5;这个查询做了将users、orders、order_items三张表连接起来。用WHERE过滤出10月份的订单。用GROUP BY按用户分组。用SUM(oi.quantity * oi.price)计算每个用户的总消费。用ORDER BY ... DESC按消费额降序排列。用LIMIT 5取前5名。假设结果如下user_idusernametotal_spent1zhangsan149.993wangwu299.002lisi199.50第二步针对这5名用户或结果中的用户找出他们各自购买数量最多的商品。这需要一个更复杂的子查询或窗口函数。为了入门我们先简化找出用户zhangsanid1购买最多的商品是什么。SELECT p.product_name, SUM(oi.quantity) AS total_quantity FROM orders o INNER JOIN order_items oi ON o.order_id oi.order_id INNER JOIN products p ON oi.product_id p.product_id WHERE o.user_id 1 GROUP BY p.product_id, p.product_name ORDER BY total_quantity DESC LIMIT 1;这个查询逻辑是找到用户1的所有订单明细按商品分组统计每个商品的购买总数量然后取数量最多的那个。将这两个步骤的思维结合起来理论上可以用更高级的SQL如窗口函数ROW_NUMBER()在一个查询里完成整个报告但这超出了入门范围。关键是理解这种“分而治之”的解决问题思路。5.2 新手常见坑点与优化建议SELECT *的滥用在正式代码或复杂查询中尽量避免使用SELECT *。明确列出需要的字段名可以减少网络传输的数据量提高查询性能也使代码意图更清晰。尤其是在表结构发生变化如增删字段时SELECT *可能导致程序出错。忘记WHERE子句在UPDATE和DELETE时这可能是灾难性的。黄金法则先SELECT后UPDATE/DELETE。即先用相同的WHERE条件执行SELECT确认影响的行是正确的再执行修改操作。NULL值的处理NULL代表缺失或未知它与任何值包括它自己的比较结果都是NULL即假。所以WHERE column NULL是错的必须用WHERE column IS NULL。在聚合函数中COUNT(column)会忽略NULL值而COUNT(*)不会。GROUP BY的困惑使用GROUP BY时SELECT列表中的非聚合列必须出现在GROUP BY子句中否则结果将是未定义的MySQL在某些模式下会报错。例如SELECT user_id, username, SUM(amount) FROM orders GROUP BY user_id如果username在user_id分组内不唯一查询可能报错或返回任意一个username值。JOIN的性能连接多张大表时性能可能成为问题。确保连接条件ON子句的字段上有索引通常是主键或外键。例如orders.user_id字段上如果有索引连接users表就会快很多。关于索引这是“慢sql优化”的核心话题我们会在后续文章中深入探讨。理解执行顺序SQL语句的书写顺序和实际执行顺序不同。一个大概的执行顺序是FROM-WHERE-GROUP BY-聚合函数-HAVING-SELECT-DISTINCT-ORDER BY-LIMIT。了解这个顺序有助于你理解为什么在WHERE子句中不能使用SELECT里定义的别名而HAVING子句可以因为HAVING在SELECT之后执行。学习SQL就像学一门新的语言初期最重要的是多写、多练、多试错。不要怕在本地数据库里折腾这是成本最低的学习方式。你可以尝试去网上找一些“sql练习题”来巩固今天学到的DDL、DML和DQL知识这是提升最快的方法。下一篇文章我们将深入探讨数据完整性约束、索引原理以及如何看懂和执行计划这些都是解决“慢sql优化”问题的关键基础。
返回列表