web前端基础到入门——14day

**摘要:**掌握 SQL 查询是数据库应用的核心。本文以实战外卖骑手数据为例,系统讲解 SELECT 查询的完整技能栈:从基础查询、条件过滤、聚合统计、分组排序,到多表关联设计。通过 20+ 个真实案例,带你从零到一掌握数据查询的精髓,让你能从任何数据库中精准捞出想要的数据。

一、 DQL 是什么

DQL(Data Query Language),数据查询语言。关键字就一个:SELECT。在一个真实业务系统里,查询占了数据库操作的 80% 以上。你打开淘宝搜"篮球鞋",后端发了一条 SELECT 去数据库;你看订单列表,又是一条 SELECT;后台老板想看今天的销售额报表,还是 SELECT。

一条完整的 DQL 语句长这样:

sql 复制代码
SELECT      -- 要查哪些字段
FROM        -- 从哪张表查
WHERE       -- 什么条件下查
GROUP BY    -- 怎么分组
HAVING      -- 分组后过滤
ORDER BY    -- 怎么排序
LIMIT       -- 取多少条

今天按这个顺序逐个拆解。先建一张练习用的表。

准备测试数据

用外卖场景------骑手表。

创建数据库和测试表:

sql 复制代码
CREATE DATABASE IF NOT EXISTS delivery_db;
USE delivery_db;

CREATE TABLE rider (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT '骑手ID',
    username VARCHAR(20) NOT NULL UNIQUE COMMENT '登录账号',
    password VARCHAR(32) DEFAULT '123456' COMMENT '密码',
    name VARCHAR(10) NOT NULL COMMENT '真实姓名',
    gender TINYINT UNSIGNED NOT NULL COMMENT '性别: 1男 2女',
    phone CHAR(11) COMMENT '手机号',
    level TINYINT UNSIGNED COMMENT '骑手等级: 1青铜 2白银 3黄金 4钻石',
    join_date DATE COMMENT '入职日期',
    city VARCHAR(20) DEFAULT '北京' COMMENT '所在城市',
    score DECIMAL(3,1) DEFAULT 5.0 COMMENT '评分',
    create_time DATETIME NOT NULL COMMENT '创建时间',
    update_time DATETIME NOT NULL COMMENT '修改时间'
) COMMENT '骑手表';

插入一些测试数据:

sql 复制代码
INSERT INTO rider (username, name, gender, phone, level, join_date, city, score, create_time, update_time) VALUES
('wangqiang',   '王强',   1, '13800001111', 4, '2020-03-15', '北京',   4.9, NOW(), NOW()),
('liumei',      '刘梅',   2, '13800002222', 2, '2022-07-01', '上海',   4.5, NOW(), NOW()),
('zhangwei',    '张伟',   1, '13800003333', 3, '2021-01-20', '北京',   4.7, NOW(), NOW()),
('chenjing',    '陈静',   2, '13800004444', 1, '2023-11-05', '深圳',   4.2, NOW(), NOW()),
('zhaolei',     '赵磊',   1, '13800005555', 4, '2019-06-10', '广州',   4.8, NOW(), NOW()),
('sunli',       '孙丽',   2, '13800006666', 3, '2021-09-12', '上海',   4.6, NOW(), NOW()),
('zhoudong',    '周东',   1, '13800007777', 2, '2022-02-28', '北京',   4.3, NOW(), NOW()),
('wujing',      '吴静',   2, '13800008888', 1, '2024-04-01', '深圳',   4.0, NOW(), NOW()),
('zhengyang',   '郑洋',   1, '13800009999', 4, '2018-11-20', '杭州',   4.9, NOW(), NOW()),
('huangli',     '黄丽',   2, '13800010000', 2, '2023-05-15', '广州',   4.4, NOW(), NOW()),
('xuming',      '徐明',   1, '13800011111', 3, '2020-08-08', '北京',   4.5, NOW(), NOW()),
('linfang',     '林芳',   2, '13800012222', 1, '2024-01-10', '上海',   3.8, NOW(), NOW()),
('maochao',     '毛超',   1, '13800013333', 2, '2021-12-25', '杭州',   4.1, NOW(), NOW()),
('heping',      '何平',   1, '13800014444', 3, '2019-04-18', '深圳',   4.6, NOW(), NOW()),
('guyan',       '顾燕',   2, '13800015555', 4, '2020-10-01', '北京',   5.0, NOW(), NOW()),
('xiaofeng',    '肖峰',   1, '13800016666', 2, '2022-09-03', '广州',   3.9, NOW(), NOW()),
('fanjie',      '范洁',   2, '13800017777', 1, '2023-03-20', NULL,     4.2, NOW(), NOW()),
('daishan',     '代山',   1, NULL,         3, '2020-05-06', '上海',   4.3, NOW(), NOW()),
('leiyu',       '雷宇',   1, '13800019999', NULL, '2021-08-14', '深圳', 3.5, NOW(), NOW()),
('dengna',      '邓娜',   2, '13800020000', 2, '2022-11-30', '北京',   4.7, NOW(), NOW());

20 条数据,故意留了几个坑:范洁的 city 是 NULL,雷宇的 level 是 NULL,代山的 phone 是 NULL。后面讲条件查询时会用到。

二、基本查询

2.1 查指定字段

sql 复制代码
SELECT name, join_date FROM rider;

只返回姓名和入职日期两列。用哪个字段就写哪个字段,别上来就 SELECT *------返回一堆不需要的列,浪费带宽和内存。

2.2 查所有字段

sql 复制代码
SELECT * FROM rider;

* 是通配符。快速看看表里有什么数据时可以用,但正式代码里别写 *------表结构一变,你的代码读到预期之外的列,排序和分页都可能出 bug。

2.3 起别名

sql 复制代码
-- 三种写法等价
SELECT name AS 姓名, join_date AS 入职日期 FROM rider;
SELECT name AS '姓 名', join_date AS '入职日期' FROM rider;
SELECT name AS "姓名", join_date AS "入职日期" FROM rider;

别名里有空格时,用单引号或双引号包起来。别名只影响显示,不改变表结构。

2.4 去重

sql 复制代码
-- 看看骑手都在哪些城市
SELECT DISTINCT city FROM rider;

结果:北京、上海、深圳、广州、杭州、NULL。DISTINCT 对整行去重。如果写 SELECT DISTINCT city, gender FROM rider,只有城市和性别完全相同的行才会被合并。

三、条件查询(WHERE)

这才是查询的灵魂。

语法:

sql 复制代码
SELECT 字段 FROM 表 WHERE 条件;

3.1 比较运算符

运算符 功能 示例
> >= < <= = 比较 score > 4.5
<> 或 != 不等于 city <> '北京'
BETWEEN a AND b 在范围内(含边界) score BETWEEN 4.0 AND 4.9
IN (v1, v2, ...) 在列表中 level IN (3, 4)
LIKE '模式' 模糊匹配 name LIKE '王%'
IS NULL / IS NOT NULL 判空 phone IS NULL

3.2 逻辑运算符

运算符 功能
AND 或 && 并且
OR 或 || 或者
NOT 或 ! 取反

3.3 实战案例

用 rider 表逐个试:

sql 复制代码
-- 1. 评分高于 4.5 的骑手
SELECT name, score FROM rider WHERE score > 4.5;

-- 2. 非北京的骑手
SELECT name, city FROM rider WHERE city <> '北京';

-- 3. 评分在 4.0 到 4.5 之间(含边界)
SELECT name, score FROM rider WHERE score BETWEEN 4.0 AND 4.5;

-- 4. 黄金或钻石等级的骑手
SELECT name, level FROM rider WHERE level IN (3, 4);

-- 5. 姓"王"的骑手 ------ % 匹配任意个字符
SELECT name FROM rider WHERE name LIKE '王%';

-- 6. 姓"王"且名字是两个字 ------ _ 匹配单个字符
SELECT name FROM rider WHERE name LIKE '王_';

-- 7. 没填手机号的骑手
SELECT name, phone FROM rider WHERE phone IS NULL;

-- 8. 有等级的骑手
SELECT name, level FROM rider WHERE level IS NOT NULL;

四、聚合统计

当我们需要对数据进行汇总计算时,就需要用到聚合函数。

4.1 常用聚合函数

函数 功能 示例
COUNT() 统计行数 SELECT COUNT(*) FROM rider;
SUM() 求和 SELECT SUM(score) FROM rider;
AVG() 求平均值 SELECT AVG(score) FROM rider;
MAX() 求最大值 SELECT MAX(score) FROM rider;
MIN() 求最小值 SELECT MIN(score) FROM rider;

4.2 实战案例

sql 复制代码
-- 1. 统计骑手总数
SELECT COUNT(*) AS 总人数 FROM rider;

-- 2. 统计有手机号的骑手数量
SELECT COUNT(phone) AS 有手机号人数 FROM rider;

-- 3. 计算平均评分
SELECT AVG(score) AS 平均评分 FROM rider;

-- 4. 找出最高评分和最低评分
SELECT MAX(score) AS 最高分, MIN(score) AS 最低分 FROM rider;

-- 5. 统计各城市骑手数量
SELECT city, COUNT(*) AS 人数 FROM rider GROUP BY city;

五、分组查询(GROUP BY)

GROUP BY 用于将数据按指定字段分组,通常与聚合函数一起使用。

5.1 基本语法

sql 复制代码
SELECT 分组字段, 聚合函数(字段)
FROM 表名
WHERE 条件
GROUP BY 分组字段;

5.2 实战案例

sql 复制代码
-- 1. 按城市分组,统计每个城市的骑手数量
SELECT city, COUNT(*) AS 人数 FROM rider GROUP BY city;

-- 2. 按等级分组,计算每个等级的平均评分
SELECT level, AVG(score) AS 平均评分 FROM rider GROUP BY level;

-- 3. 按性别和城市分组,统计人数
SELECT gender, city, COUNT(*) AS 人数 
FROM rider 
GROUP BY gender, city;

5.3 HAVING 子句

HAVING 用于对分组后的结果进行过滤,类似于 WHERE,但作用于分组后的数据。

sql 复制代码
-- 找出平均评分大于 4.5 的城市
SELECT city, AVG(score) AS 平均评分
FROM rider
GROUP BY city
HAVING AVG(score) > 4.5;

-- 找出骑手数量超过 3 人的城市
SELECT city, COUNT(*) AS 人数
FROM rider
GROUP BY city
HAVING COUNT(*) > 3;

六、排序(ORDER BY)

ORDER BY 用于对查询结果进行排序。

6.1 基本语法

sql 复制代码
SELECT 字段
FROM 表名
ORDER BY 排序字段 [ASC|DESC];

ASC:升序(默认),DESC:降序。

6.2 实战案例

sql 复制代码
-- 1. 按评分降序排列
SELECT name, score FROM rider ORDER BY score DESC;

-- 2. 按入职日期升序排列
SELECT name, join_date FROM rider ORDER BY join_date ASC;

-- 3. 多字段排序:先按城市升序,再按评分降序
SELECT name, city, score 
FROM rider 
ORDER BY city ASC, score DESC;

七、分页(LIMIT)

LIMIT 用于限制查询结果的数量,常用于分页查询。

7.1 基本语法

sql 复制代码
SELECT 字段
FROM 表名
LIMIT 偏移量, 记录数;

或者:

sql 复制代码
SELECT 字段
FROM 表名
LIMIT 记录数 OFFSET 偏移量;

7.2 实战案例

sql 复制代码
-- 1. 查询前5条记录
SELECT * FROM rider LIMIT 5;

-- 2. 查询第6-10条记录(每页5条,第2页)
SELECT * FROM rider LIMIT 5, 5;

-- 3. 按评分降序排列,取前3名
SELECT name, score FROM rider ORDER BY score DESC LIMIT 3;

八、多表关系设计

实际业务中,数据通常分布在多张表中,需要通过关联查询获取完整信息。

8.1 表关系类型

  • 一对一:一个表中的一条记录对应另一个表中的一条记录
  • 一对多:一个表中的一条记录对应另一个表中的多条记录
  • 多对多:需要中间表来建立关联

8.2 创建关联表

继续使用外卖场景,创建订单表:

sql 复制代码
CREATE TABLE orders (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT '订单ID',
    order_no VARCHAR(20) NOT NULL UNIQUE COMMENT '订单号',
    rider_id INT UNSIGNED COMMENT '骑手ID',
    amount DECIMAL(10,2) NOT NULL COMMENT '订单金额',
    status TINYINT UNSIGNED DEFAULT 1 COMMENT '状态: 1待接单 2配送中 3已完成 4已取消',
    create_time DATETIME NOT NULL COMMENT '创建时间',
    FOREIGN KEY (rider_id) REFERENCES rider(id) ON DELETE SET NULL
) COMMENT '订单表';

8.3 关联查询(JOIN)

JOIN 用于将多个表中的数据关联起来。

8.3.1 INNER JOIN(内连接)

只返回两个表中匹配的记录。

sql 复制代码
-- 查询骑手及其配送的订单
SELECT r.name AS 骑手姓名, o.o
相关推荐
谷哥的小弟1 小时前
TypeScript类型断言
前端·javascript·typescript
智塑未来1 小时前
鸿蒙系统小红书隐私保护——应用锁开启指南
服务器·前端·harmonyos
福大大架构师每日一题2 小时前
agno v2.8.7 发布:顾问模型、精准路线、调度能力全面升级,10项关键修复一次看懂
java·开发语言·数据库
深念Y2 小时前
老Flyme 官方 root 开启 ADB TCP 方案
linux·数据库·网络协议·tcp/ip·adb·智能手机·emmc
c2385610 小时前
MySQL 基础用法(上):库表管理与数据增删改
c语言·数据库·c++·mysql
东风破_10 小时前
React 受控组件与非受控组件:从一个输入框讲清表单数据流
前端
尤乐娃子10 小时前
进入大厂(厂子大)实习Day12
前端·笔记·实习
程序员爱钓鱼10 小时前
Rust Copy详解:隐式复制与轻量数据类型
前端·后端·rust
jnrjian11 小时前
postgres database 更换用户
数据库·postgresql