跳转至
MySQLRedisNumPyPandasMatplotlib数据分析

MySQL、Redis 与数据分析学习笔记

这份文档按照“关系型数据库 → 内存数据存储 → 数值计算 → 表格数据处理 → 数据可视化”的顺序编排,覆盖 MySQL、Redis 和 Python 数据分析三剑客。

第一部分:MySQL

1. DDL 与 DML

库的相关操作

库的定义

用于存放数据表的容器,里面可以包含各种数据表

(新增)创建库
# 语法
create database [if not exists] 库名 [ character set utf8];
删除库
#语法
drop database [if exists] 库名;
查看所有的数据库
#语法
show databases;

执行效果:1778987657500

使用数据库
#语法
use 库名

#1.再创建表之前,一定要使用数据库,切换到对应的数据库,再去创建表,这样表才能创建到对应的库下面
#2.创建表的时候标记,库.表名,也可以创建到指定的库下面

表的相关操作

表的定义

用于存放数据的基本单元,行列式的结构,类似于excel表格

(新增)创建表
#语法:
create .table [if not exists] 表名(
    字段1    类型    [约束],
    字段2    类型    [约束],
    字段3    类型    [约束],
    .......
);

类型的梳理

  • 整数-int
  • 小数-decimal(m,d),d表示小数固定是几位,m表示小数+整数最多是几位
  • 时间-
  • 年月日-date
  • 时分秒-time
  • 年月日+时分秒-datetime
  • 年月日+时分秒(时间戳)-timestamp(1970年起)
  • 文本-varchar(文本长度)

约束的梳理:

  • 主键约束(primary key)-不能为空且不能重复
  • 非空约束-不能为空
  • 唯一约束-不能重估
  • 默认值约束-即使没有设置这个字段的值,也会有一个默认的初始值
  • 外键约束-多表关联的时候才会使用
#注意,记得先使用数据库再创建
create table if not exists goods_info(
    #primary key表示主键
    id        int          primary key ,
    name      varchar(50),
    category  varchar(10),
    price     decimal(10,2),
    date      date
);
删除表
#语法:
drop table [if exists] .表名;
查看表
#查看所有的表(当前使用的库)
show tables;
#查看指定的表结构
desc .表名;

学会找规律:

​ use 库名

​ desc 表明

这两个关键字没有标识到底是table还是database,意味着这两个关键字都只能给对应的库或者表使用!

​ drop table

​ drop database

​ create table

​ create database

这些关键字,有库或者表的标识,表示,既可以给库用,又可以给表用!

修改表
新增表的列
#语法
#alter table 库.表名 add 列名 类型 [约束];
alter table shop_db.goods_info add isVIP  varchar(1);
#查看新增后的结构
desc shop_db.goods_info;
删除表的列
#语法
#alter table 库.表名 drop 列名;
alter table shop_db.goods_info drop date;
#查看删除后的结构
desc shop_db.goods_info;
修改表的列
#语法
alter table .表名 change 旧的列名 新的列名 类型 [约束];
# 技巧:
# 1.可以只改列名,也可以只改类型,也可以只改约束!!!
alter table shop_db.goods_info change id good_id int; #改列名
alter table shop_db.goods_info change good_id good_id varchar(5); #改类型
# 2.修改 = 删除 + 新增
alter table shop_db.goods_info change (id:删除的东西) (good_id int:新增的东西);
修改表的名称(了解)
#语法
rename table 旧的表名 to 新的表名
#了解,后续大模型开发基本不会修改表名,但是得知道可以进行修改
rename table shop_db.goods_info to shop_db.shop_info;
表内容的清空
#语法  truncate 库.表名
truncate shop_db.product

DDL综合练习

# DDL综合练习(设计一个数据库,包含多张业务表)
# 设计一个电商系统数据库(shop_db),包含以下核心功能:
# 1. 用户管理
# 2. 商品分类管理
# 3. 商品信息管理
# 4. 订单管理

# 具体需求:
-- 综合案例:
-- DDL的综合案例
-- 1. 创建数据库并使用: shop_db
create database shop_db;
use shop_db;
-- 2. 创建用户表(user): user_id  username password email phone
create table user (
  user_id  varchar(5) primary key ,
  username varchar(20),
  password varchar(20),
  email  varchar(50),
  phone  varchar(20)
);
-- 3. 创建商品分类表(category): category_id cname description
create table category (
  category_id  varchar(5) primary key ,
  cname varchar(10),
  description varchar(100)
);
-- 4. 创建商品表(product):  product_id   product_name price category_id
create table product (
  product_id  varchar(5) primary key ,
  product_name varchar(20),
  price decimal(10,2),
  category_id varchar(5)
);
-- 5. 创建订单表(order): order_id  user_id total_amount status
# order是mysql里面的关键字,所以不能直接使用
# 1.要么库.表(推荐)
# 2.要么`表名`
create table `order` (
  order_id  varchar(5) primary key ,
  user_id varchar(5),
  total_amount decimal(20,2),
  status varchar(20)
);
-- 6. 查看所有表(明确在哪个库)
show tables;
-- 7. 查看商品表结构
desc product;
-- 8. 修改表结构操作
-- 8.1 为用户表添加真实姓名列 real_name
alter table shop_db.user add real_name varchar(20);
desc user;
-- 8.2 删除分类表的描述列
alter table shop_db.category drop description;
desc category;
-- 8.3 重命名订单表 order 重命名为  order_info
rename table `order` to order_info;
-- 9. 最终查看所有表
show tables ;

DML语法

新增数据

指定新增某几个字段的一条数据
# 语法:值一定要和字段对应上
# insert into 库.表名(字段1,字段2...) values (值1,值2)
# 示例:
insert into shop_db.product(product_id,product_name) values('c0001','Mate80');
新增所有字段的一条数据
# 语法:值一定要和所有的字段对应上
# insert into 库.表名  values (值1,值2...)
# 示例:
insert into shop_db.product values ('c0002','MacBook',8000.00,'0001');
新增所有字段的多条数据
# 语法:值一定要和所有的字段对应上
# insert into 库.表名  values (值1,值2...),(值1,值2...),(值1,值2...)
# 示例:
insert into shop_db.product values
                                   ('c0006','小天才',8000.00,'0002'),
                                   ('c0007','小天才',8000.00,'0002'),
                                   ('c0008','小天才',8000.00,'0002'),
                                   ('c0009','小天才',8000.00,'0002'),
                                   ('c0010','小天才',8000.00,'0002'),
                                   ('c0011','小天才',8000.00,'0002');

删除数据

删除所有数据
# 语法:
# delete from 库.表名
delete from shop_db.product;

#truncate初始化(崭新的表)
#delete删除(表的内容时空的,但是表会有操作过的记录)
删除指定数据
# 语法:
# delete from 库.表名 where 条件
# 需求:删除小天才
delete from shop_db.product where product_name = '小天才';

修改数据

修改某个字段的所有数据
# 语法:
# update 库.表名 set 字段 = 值
update shop_db.product set product_name = '小天才';
修改某个字段的某些数据
# 语法:
# update 库.表名 set 字段 = 值 where 条件
update shop_db.product set price = 6000 where product_id = 'c0001';
修改多个字段的某些数据
# 语法:
# update 库.表名 set 字段1 = 值1,字段2 = 值2 where 条件
update shop_db.product set price = 5000,category_id = '0000' where product_id = 'c0001';

关于null(空)数据的操作

为空 - 不能用=,而是使用is null
delete from shop_db.product where price is null; #删除价格为null的商品
不为空 - 不能用!=,而是使用is not null
delete from shop_db.product where price is not null; #删除价格不为null的商品

条件的一些说明

逻辑判断

或or、与and、非not

你:今天吃什么?有想法吗?
朋友:米线、螺蛳粉、或者酸辣粉     (或:满足其中一个就可以)            or
你:不吃螺蛳粉                  (非:只要不是这一个就可以)           not
朋友:那就吃酸辣粉               (且:酸辣粉必须又酸又辣)            and


# 练习:删除所有 **species 为 '狗' 且 status 为 '健康'** 的宠物记录
delete from . where species = '狗' and status = '健康'

2. SQL 约束

SQL约束的意义:建表的时候加了约束,目的是在插入数据的时候做限制

主键约束(Primary Key)

创建主键约束(2种方法:创建表的时候设置(√) + 已有表的时候设置(√))

建表的时候,直接约束某个字段

create table my_tb (
    id int primary key(主键约束)
);

建表的时候,在统一设置的区域进行约束

create table my_tb(
    id int,
    #统一设置的区域:字段的最后 contraint 是可以省略的
    [constraint] primary key (字段)
);

两个效果:钥匙(唯一性),小黑点(非空性)

删除主键约束(1种方法)

删除已有主键的表的主键

# 语法:只能删除唯一性
alter table .表名 drop primary key

alter table day03_db.product drop primary key; #唯一性
# 补充:删除非空性
alter table day03_db.product change p_id p_id int;#非空性

#与删除表的列非常相似
alter table .表名 drop 列名
已有表,添加主键

需要先将表字段不满足约束的值删掉,才能设置主键

# 语法:
alter table .表名 add primary key (列名);
alter table day03_db.product add primary key (p_id);
alter table day03_db.user add primary key (user_id);

#与新增表的列非常相似
alter table .表名 add 列名 类型;
自动增长列(无法单独使用,也不是一个约束)

结合主键进行使用,可以让我们的列再主键没有传递或传递null的时候基于上一条数进行自动增

#建表的时候需要设置这个自动增长列
create table day03_db.category(
  #分类的id  类型           主键约束       自动增长(辅助性,不是约束)
  cid       int            primary key   auto_increment,
  c_name    varchar(10)
);

# 如果上设置了自动增长的主键,传递为null的时候,会自动基于上一条数据+1,如果第一次插入,默认是从1开始的!
insert into day03_db.category values (null,'手机');
insert into day03_db.category(c_name) values ('手机');


# 也可以修改这个自动增长的默认值(了解,一般不会修改,改的话也是只改第一次)
alter table .表名 auto_increment = 初始值

其他约束

非空约束(1种方法)

创建(2种:建表的时候,已有的表添加)+删除(1种)

# 语法:建表时候添加
create table .表名 (
    id     int            primary   key auto_increment,
    name   varchar(10)    not null  #效果就是约束该列不能有null值
);

# 语法:已有表进行添加
alter table .表名 change 列名 列名 类型 not null;

# 语法:删除(和主键不一样,键才须有drop)
alter table .表名 change 列名 列名 类型;
唯一约束(1种方法)

创建(2种:建表的时候,已有的表添加)+删除

# 建表时候的添加
create table .表名(
    id      int          primary key  auto_increment,
    c_sn    varchar(10)  unique # 1.字段后面添加
    c_mac   varchar(10),
    c_id    varchar(10),
    constraint  unique (c_mac), # 2.在公共区域添加
    constraint  unique (c_id),  # 可以使用多次
)

# 删除
alter table .表名 drop key c_mac;


# 已有表的添加
alter table .表名 add unique (c_mac);


# 总结:学习的路径
# 1.建表的时候添加约束
# 2.删除约束约束
# 3.已有表进行添加约束
默认值约束(1种方法)

创建+删除

# 建表的时候设置默认值
create table .表名 (
   user_name  default '匿名用户',
   city default '北京'
)
# 删除:
# alter table 库.表名 change 列名 列名 类型; #取消默认值约束(键才drop,其他的都可以change)
外键约束(后面重点讲)

联合(键:主键 + 唯一) - 多个字段共同达成

联合主键(1种方法)- 多个字段共同形成一个主键
# 效果:
# 1.每个字段都不为空
# 2.每个字段的值拼起来不重复(18位身份证号:6位省市区  8位出生年月日 4位序号)

# 要求:新建一个身份证号数据库,area地区  birth出生信息  code每个人的序号

# 联合主键语法:只能通过公共区域进行设置
create table .表名 
   area     varchar(6),
   birth     varchar(8),
   code      varchar(4),
   #公共区域
   constraint primary key (area,birth,code)


#删除
alter table .表名 drop primary key

创建+删除

联合唯一(1种方法)

创建+删除

# 效果:
# 每个字段的值拼起来不重复(18位身份证号:6位省市区  8位出生年月日 4位序号)

# 要求:新建一个身份证号数据库,area地区  birth出生信息  code每个人的序号

# 联合主键语法:只能通过公共区域进行设置
create table .表名 
   area     varchar(6),
   birth     varchar(8),
   code      varchar(4),
   #公共区域
   constraint unique (area,birth,code)

#删除
alter table .表名 drop key 约束名

3. DQL 单表查询

单表查询(基本语法)

select 列 from 表

1.查询1列
select  cname  from  .
2.查询n列
select  cid,cname  from  .
3.查询所有列
select * from .
4.去重(一般是针对某一列去重)
select distinct cname  from  .
5.起别名
select distinct p.cname as '分类名称'  from  . as p
6.优化写法,省略as
select distinct p.cname '分类名称'  from  . p
条件查询
# 1.比较
        >  <  >=  <=  =  !=
# 2.区间
        between xxx and xxx  连续区间      in (xxx,xxx)  非连续区间
# 3.相似
        like  '_'表示一个字符 '%' 0个或多个字符
# 4.非空
        is  null 为空<null>   is not null  不为空
# 5.逻辑
        and    or    not  
排序查询
# order by  字段1 asc(升序)|desc(降序) ,字段2 asc(升序)|desc(降序)

# 拓展:字符串比对
# 比对原则,逐个字符比对

# order by可以结合where使用  ,先写where后写order by
聚合查询
# 语法:
select count(字段) from .
#常见的聚合函数
count  求数据的行数(不会统计null)
max    求数据的最大值
min    求数据的最小值
sum    求数据的总和
avg    求数据的平均值

注意点:查询的时候聚合了,那么只能展示聚合的!要聚合都聚合,要不聚合都不聚合!

count(1)   count(*)  统计行数的写法
count(1)不会去表里挨个查数据是不是为null,直接拿1做判断,性能会好
总结:统计null count(1)  不统计null count(列名)
分组查询
1.分组(整合数据:以组为单位)  聚合(处理业务:查看组的信息)
2.以什么分组就以什么查询,查询的列必须是分组的字段,或者是聚合的函数

#语法:group by
select grade,class,avg(score) as '平均分' from company_db.student group by grade,class having avg(score) > 90
分页查询(不是SQL,是mysql的方言)
# 语法:
limit [从哪开始(默认是0,表示第一条数据)]  取多少条
limit [start]  num

# 需求1:从student表获取前10条数据
select * from company_db.student limit 10;
# 需求2:从student表获取11~20条数据
select * from company_db.student limit 10,10;
综合案例
# 综合练习
-- -------------------------------------------------------准备工作
-- 创建数据库
CREATE DATABASE IF NOT EXISTS school_db;
USE school_db;

-- 创建学生成绩表
CREATE TABLE student_scores (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    gender ENUM('男','女') DEFAULT '男',
    class VARCHAR(20) NOT NULL COMMENT '班级',
    subject VARCHAR(30) NOT NULL COMMENT '科目',
    score DECIMAL(5,2) NOT NULL COMMENT '成绩'
);

-- 插入测试数据
INSERT INTO student_scores (name, gender, class, subject, score) VALUES
('张三', '男', '高三(1)班', '数学', 92.5),
('李四', '男', '高三(2)班', '数学', 88.0),
('王芳', '女', '高三(1)班', '数学', 95.5),
('赵敏', '女', '高三(3)班', '数学', 76.0),
('刘伟', '男', '高三(2)班', '英语', 82.5),
('陈静', '女', '高三(1)班', '英语', 91.0),
('杨洋', '男', '高三(3)班', '英语', 68.5),
('周杰', '男', '高三(1)班', '物理', 87.0),
('林琳', '女', '高三(2)班', '物理', 93.5),
('郭强', '男', '高三(3)班', '物理', 79.0),
('马超', '男', '高三(2)班', '数学', 85.5),
('黄蓉', '女', '高三(1)班', '英语', 89.0),
('诸葛亮', '男', '高三(3)班', '物理', 98.5);

-- 相关需求:
-- 1. 排序查询:按成绩降序查看所有数学成绩
-- 条件筛选:  筛选出数学成绩
-- 排序操作: 成绩的降序

select name, score as '分数' from school_db.student_scores where subject = '数学' order by score desc ;

-- 2. 聚合查询:统计物理科目的最高分、最低分、平均分
-- 条件筛选: 筛选出 物理
-- 获取期物理中 最高 最低 平均
# 补充:如果列用了字符类的数据,那么每一行都将用这个字符进行填充,如果表头想自定义还可以用as起个别名
select '学科 - 物理' as '学科',max(score) as '最高分',min(score) as '最低分',avg(score) as '平均分' from school_db.student_scores where subject = '物理';

-- 3. 分组基础:统计每个班级的学生人数
-- 分组字段: 班级
-- 学生人数: 只需要看这个组内有几条数据即可 count()
select class '班级',count(1) as '人数' from school_db.student_scores group by class;


-- 4. 分组+聚合:计算每门科目的平均分(保留1位小数)
-- 分组字段: 科目(subject)
-- 平均分:  成绩 使用 avg
select subject,avg(score) from school_db.student_scores group by subject;

-- 5. [] 多级分组:统计每个班级每门科目的最高分
-- 分组字段: class  subject
-- 聚合内容: 成绩的最高分
select class,subject,max(score) from school_db.student_scores group by class,subject order by class;


-- 6. [] 分组+条件:找出女生平均分超过90的科目 :
-- 条件筛选:  找到班级中所有的女生
-- 计算出每个科目的平均分:  分组字段: 科目  聚合操作: 成绩的平均分
-- 在分组聚合后结果上, 找到大于90分的数据
select subject,avg(score) as avg_score from school_db.student_scores where gender = '女' group by subject having avg_score >90 ;

多表查询(概念)

外键
一对一
一对多
多对多

4. 多表查询

外键约束

  • 外键约束:约束的是从表,约束从表不能插入主表主键不存在的外键数据
# 主表
create table tableA (
    id  int   primary key ,
    name varchar(10)
)
# 从表
create table tableB(
    id  int,
    cname varchar(10),
    #建立主表和从表的约束关系
    foreign key id references tableA(id)
)

# 约束:限制从表插入数据时,外键不能设置主表主键不存在的值
# 企业开发过程中,更多是通过个人素质进行约束,这里学习外键只是告诉大家,表之间是可以有关联关系的!

多表查询

无条件连接(了解)
  • 交叉连接(笛卡尔积)
# 笛卡尔积(无实际应用场景,只有写错了才会出现)
select * from 表A,表B

# 笛卡尔积
select * from 表A join 表B

# 数据的暴力拼接,表A10000条 表B10000条   10000 * 10000 = 100000000
有条件连接
  • 内连接
select * from 表A join 表B  on 表A.字段  =  表B.字段

得到的结果是:表A  表B  交集
表A 10   表B 10  ,内连接后最多10条,有可能0条
  • 左外连接
select * from 表A left join 表B  on 表A.字段  =  表B.字段

得到的结果是:
以表A为主拼接表B  如果表A有,表B没有匹配到的,会有<null>进行拼接
表A 10   表B 20  ,左外连接后最多20条,最少10条
  • 右外连接
select * from 表A right join 表B  on 表A.字段  =  表B.字段

得到的结果是:
以表B为主拼接表A  如果表B有,表A没有匹配到的,会有<null>进行拼接
表A 30   表B 20  ,右外连接后最多30条,最少20条
  • 全连接
union  (去重)   union all
# 作用:union是进行数据的拼接
#全连接:
select * from 表A left join 表B  on 表A.字段  =  表B.字段
union
select * from 表A right join 表B  on 表A.字段  =  表B.字段;

自连接查询

  • 自己关联自己
核心:多次外连接查询同一张表
使用场景:树形节点(组织架构、省市区)

select * from area.new_areas sheng
  join area.new_areas shi on sheng.id = shi.pid
  join area.new_areas qu on shi.id = qu.pid
;
代码验证:
#建表
create table area.new_areas (
  id    varchar(5),
  title varchar(5),
  pid   varchar(5)
);
#插入数据
insert into area.new_areas
values ('1', '陕西省', null),
       ('2', '河南省', null),
       ('3', '西安市', '1'),
       ('4', '郑州市', '2'),
       ('5', '雁塔区', '3'),
       ('6', '金水区', '4');
内连接(连接+筛选)/左外连接(硬连) - 不论连接几次,都是再做数据的拼接
select * from area.new_areas sheng;  # 6,一张表6条数据
select * from area.new_areas sheng
  join area.new_areas shi on sheng.id = shi.pid;  #4, 6join6 = 4 ,只有4条数据被关联到了
select * from area.new_areas sheng
  join area.new_areas shi on sheng.id = shi.pid
  join area.new_areas qu on shi.id = qu.pid;  # 2,4join6 = 2,只有2条数据被关联到了

子查询

  • 将查询结果作为新的查询的表再次查询
使用场景:
1.from后面
# 第一次查询的结果
select pname, price from day05_mutiple_tab.table_product;
# 将第一次查询的结果作为第二次查询的内容
select t.price
from (select pname, price from day05_mutiple_tab.table_product) t
where price > 2000;
#使用说明:
# 问题:计算((1+2)/3*4)/5+6*3-21
# 问题:计算1+2     计算3*4   计算1+2的结果/3*4的结果

2.where后面(第一次查询的结果必须是一个值,一般都是通过聚合函数得到的结果)
# 需求:查询商品价格大于平均价格的有哪些?
# 1.查平均价格
select avg(price)
from day05_mutiple_tab.table_product;
# 2.筛选大于平均价格
select *
from day05_mutiple_tab.table_product
where price > (select avg(price) from day05_mutiple_tab.table_product);


3.select后面(第一次查询的结果必须是一个值,一般都是通过聚合函数得到的结果)
# 需求:计算一下每个商品与平均价格的差价
# 1.求平均价格
select avg(price)
from day05_mutiple_tab.table_product;
# 2.展示计算差价
select price - (select avg(price) from day05_mutiple_tab.table_product)
from day05_mutiple_tab.table_product;

API内容:

join / join on / left join on / right join on /union /union all

案例
  • 省市区案例
create table area.new_areas (
  id    varchar(5),
  title varchar(5),
  pid   varchar(5)
);

insert into area.new_areas
values ('1', '陕西省', null),
       ('2', '河南省', null),
       ('3', '西安市', '1'),
       ('4', '郑州市', '2'),
       ('5', '雁塔区', '3'),
       ('6', '金水区', '4');

# 思想1:先硬拼,做减法
select *
from area.new_areas sheng
       left join area.new_areas shi on sheng.id = shi.pid
       left join area.new_areas qu on shi.id = qu.pid
where sheng.pid is null
;

# 思想2:拼的时候就按照条件拼
select * from area.new_areas sheng
  join area.new_areas shi on sheng.id = shi.pid
  join area.new_areas qu on shi.id = qu.pid
;
  • 员工任务表案例
-- 创建员工表
CREATE TABLE employees (
  employee_id INT PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  department VARCHAR(50)
);

-- 创建项目表
CREATE TABLE projects (
  project_id INT PRIMARY KEY,
  project_name VARCHAR(100) NOT NULL,
  manager_id INT,
  FOREIGN KEY (manager_id) REFERENCES employees(employee_id)
);

-- 创建任务表
CREATE TABLE tasks (
  task_id INT PRIMARY KEY,
  task_name VARCHAR(100) NOT NULL,
  project_id INT,
  assignee_id INT NULL, -- 注意:允许为NULL
  FOREIGN KEY (project_id) REFERENCES projects(project_id),
  FOREIGN KEY (assignee_id) REFERENCES employees(employee_id)
);

-- 插入员工数据
INSERT INTO employees (employee_id, name, department) VALUES
(1, '张三', '研发部'),
(2, '李四', '研发部'),
(3, '王五', '市场部'),
(4, '赵六', '产品部'),
(5, '钱七', '研发部');

-- 插入项目数据
INSERT INTO projects (project_id, project_name, manager_id) VALUES
(101, '电商平台重构', 1), -- 经理是张三
(102, '移动App推广', 3),  -- 经理是王五
(103, 'AI助手开发', 1);   -- 经理也是张三

-- 插入任务数据
INSERT INTO tasks (task_id, task_name, project_id, assignee_id) VALUES
(1001, '数据库设计', 101, 2),    -- 分配给李四
(1002, '后端API开发', 101, 1),   -- 分配给张三(经理自己也干活)
(1003, '官网海报设计', 102, NULL), -- 未分配
(1004, '社交媒体投放', 102, 3),   -- 分配给王五(经理)
(1005, '算法模型训练', 103, 5),   -- 分配给钱七
(1006, '前端界面优化', 101, NULL), -- 未分配
(1007, '用户需求调研', 103, 4);   -- 分配给赵六

# 理解业务:要干什么?翻译要做的事
-- 需求1: 查询所有已分配的任务的详细信息,要求显示:任务名称、所属项目名称、任务负责人姓名。 (三个表联查 基于中间件关联另外二个张 内连接)
#表一旦起了别名,就只能使用别名,不能使用原名
# 思想:一步一步去实现
# 1.查询所有已分配的任务
# select * from company_db.tasks where assignee_id is not null ;
# 2.显示:任务名称
# select t.task_name from company_db.tasks t where assignee_id is not null ;
# 3.显示:所属项目名称
# select t.task_name,p.project_name from company_db.tasks t left join projects p on t.project_id = p.project_id
# where assignee_id is not null ;
# 4.显示:任务负责人姓名
select t.task_name '任务名称',p.project_name '所属项目',e.name '负责人'
from company_db.tasks t
              left join projects p on t.project_id = p.project_id
              left join employees e on t.assignee_id = e.employee_id
where assignee_id is not null ;

# 思想2:先拼接,后筛选,先减行再减列
select t.task_name,p.project_name,e.name from company_db.tasks t
          left join projects p on t.project_id = p.project_id
          left join employees e on t.assignee_id = e.employee_id
where t.assignee_id is not null;
-- 需求2: 列出所有项目及其对应的任务信息,即使某些项目下暂时还没有创建任何任务。 (项目 和 任务的 左外连接)
# 思想:以项目为左表,再进行左外连接任务
select * from projects p left join tasks t on p.project_id = t.project_id;
-- 需求3: 查询所有员工及其被分配的任务情况,确保所有员工都出现,即使他/她还没有被分配任何任务。(员工和任务的左外连接)
# 思想:以员工为左表,再进行左外连接任务
select * from employees e left join tasks t on e.employee_id = t.assignee_id;

-- 需求4: 查询每个任务的详细信息,包括:任务名称、所属项目名称、项目经理姓名、任务负责人姓名。(以任务为主表, 关联项目和员工,  任务的所有信息都要显示, 要全部使用左外连接)
# 思想:以任务为主表,左连接项目表+员工表
select t.task_name '任务名称',p.project_name '所属项目名称',e.name '项目经理姓名',e2.name '任务负责人姓名'
from tasks t
      left join projects p on t.project_id = p.project_id
      left join employees e on p.manager_id = e.employee_id
      left join employees e2 on t.assignee_id = e2.employee_id;
-- 需求5: 将所有员工和所有任务进行关联,最终显示所有可能的组合。(员工和任务的 左连 + 右连 + union)
-- 所有交叉可能的情况
#  思想:以员工为主查任务 + 以任务为主查员工(连接2次,因为有2个姓名字段)
select * from employees e left join tasks t on e.employee_id = t.assignee_id
union
select * from employees e right join tasks t on e.employee_id = t.assignee_id;

5. MySQL 内置函数

数值函数

小数位(5)
round()四舍五入

format(要截取的数,保留几位小数) 小数截取(四舍五入)

truncate(要截取的数,保留几位小数) 小数截取

ceil() 向上取整

floor() 向下取整
数值计算(3)
mod(被除数,除数) 取余数

pow(底数,指数) 求幂

rand([种子]) 随机数0~1

字符函数

大小写转换(2)
upper(字符串) 转大写
lower(字符串) 转小写
反转、替换、重复、拼接(5)
reverse(字符串)  反转
replace(字符串,要替换的内容,替换成什么) 替换
repeat(字符串,重复几次)  重复
concat(字符串1,字符串2....)  拼接
concat_ws(标识符,字符串1,字符串2....)  用符号的拼接
截取(4)
substr(字符串,截取的长度)    1就是第1位,-1就是最后一位
substring(字符串,截取的长度)   1就是第1位,-1就是最后一位
left(字符串,截取的长度)   从左侧截取几位
right(字符串,截取的长度)   从右侧截取几位
长度(1)
length(字符串)  统计字符串长度

日期函数

获取当前时间(3)
now()  获取当前年月日时分秒
current_date 获取当前年月日
current_time 获取当前时分秒
计算时间差(4)
date_add(日期,interval 1 YEAR) 计算时间顺延(后)
date_sub(日期,interval 1 DAY) 计算时间回滚(前)
datediff(日期1,日期2)  计算两个时间的差(单位是天)
timestampdiff(维度,日期1,日期2)  计算两个时间的差(维度可以自定义)
获取具体的时间(7)
year(日期)
month(日期)
day(日期)
hour(日期)
minute(日期)
second秒
weekday星期  0~6表示周1到周日
时间转换(4)
字符串  时间的转换
date_format(时间,格式化方式)   %Y 2026 %y 26  %M May %m 05   %D 25th %d 25 ...%i 分钟
str_to_date(字符串,格式化方式)  %Y 2026 %y 26  %M May %m 05   %D 25th %d 25 ...%i 分钟
时间戳  时间的转换
unix_timestamp()    获取当前的时间戳()
unix_timestamp([日期])    获取指定日期的时间戳()
from_unixtime(时间戳)      根据时间戳转换为日期

要求:能记住mysql的内置函数都能做哪些事!!!38个API记不住情有可原!!!

条件语句(2)

if语句(双分支语句)
语法:if(表达式,成立的值,不成立的值)
case when语句(多分支语句)
语法:
case
    when 表达式1 then 表达式成立的值1
    when 表达式2 then 表达式成立的值2
    when 表达式3 then 表达式成立的值3
    when 表达式4 then 表达式成立的值4
    else  以上表达式都不成立时的值
end

经典面试题

行转列(多行转多列、多行转一行)
思路1:暴力枚举,列于3个科目组合的所有可能,再做筛选(保底)
select t1.xuehao,t1.chengji,t2.chengji,t3.chengji  from score t1
join score t2 on t1.xuehao  = t2.xuehao
join score t3 on t1.xuehao  = t3.xuehao
where t1.kemu = '语文' and t2.kemu = '数学' and t3.kemu = '英语'

思路2:求每个人对应科目的成绩,聚合(推荐)
select xuehao from score;#6条数据
select xuehao,if(kemu='语文',chengji,0) from score group by xuehao;#2条数据,但是会报错。因为分组必聚合
select xuehao,sum(if(kemu='语文',chengji,0)) from score group by xuehao;#2条数据,但是只有语文成绩
select xuehao,sum(if(kemu='语文',chengji,0)),sum(if(kemu='数学',chengji,0)),sum(if(kemu='英语',chengji,0)) from score group by xuehao;#2条数据,但是表头没有改名字
select xuehao,sum(if(kemu='语文',chengji,0)) '语文',sum(if(kemu='数学',chengji,0)) '数学',sum(if(kemu='英语',chengji,0)) '英语' from score group by xuehao;#2条数据,但是表头没有改名字
列转行(多列转多行、一行转多行)
思想:
1.凑出学号(2条)
2.凑出语文科目
3.凑出语文成绩(2条)
union
1.凑出学号(2条)
2.凑出数学科目
3.凑出数学成绩(2条)
union
1.凑出学号(2条)
2.凑出英语科目
3.凑出英语成绩(2条)


select xuehao,'语文'as '科目',yuwen from w_score
union
select xuehao,'数学'as '科目',shuxue from w_score
union
select xuehao,'英语'as '科目',yingyu from w_score;

第二部分:Redis

Redis 是基于内存的数据存储,默认端口为 6379,常用于缓存、计数、排行榜和临时状态存储。

1. 常见数据类型

常见数据类型与命令:

类型 常用命令
字符串 SETGETMSETMGETSETEX
数值计数 INCRDECRINCRBYDECRBY
哈希 HSETHGETHMGETHGETALLHKEYSHVALSHDEL
列表 LPUSHRPUSHLPOPRPOPLLENLRANGE
集合 SADDSPOPSREMSISMEMBERSMEMBERSSCARDSINTERSUNIONSDIFF
有序集合 ZADDZSCOREZRANKZRANGEZREVRANGEZRANGEBYSCORE

2. 字符串与计数

# 保存和读取字符串
SET model:qwen:status running
GET model:qwen:status

# 一次设置和读取多个值
MSET model:qwen:port 11434 model:llama:port 11435
MGET model:qwen:port model:llama:port

# 设置带过期时间的缓存,单位为秒
SETEX answer:1001 300 "这是一条缓存回答"

# 计数
SET api:request_count 0
INCR api:request_count
INCRBY api:request_count 10
DECR api:request_count

3. 哈希

哈希适合保存一个对象的多个字段:

HSET model:qwen name qwen temperature 0.7 context_length 32768
HGET model:qwen name
HMGET model:qwen temperature context_length
HGETALL model:qwen
HKEYS model:qwen
HVALS model:qwen
HDEL model:qwen temperature

4. 列表

列表有顺序,可以从左右两端添加或取出元素:

LPUSH chat:messages "第一条消息"
RPUSH chat:messages "第二条消息"
LRANGE chat:messages 0 -1
LLEN chat:messages
LPOP chat:messages
RPOP chat:messages

5. 集合

集合中的成员不重复,适合去重和集合关系计算:

SADD user:1:skills python rag agent
SMEMBERS user:1:skills
SISMEMBER user:1:skills rag
SCARD user:1:skills
SREM user:1:skills agent

SINTER user:1:skills user:2:skills
SUNION user:1:skills user:2:skills
SDIFF user:1:skills user:2:skills

6. 有序集合

有序集合为每个成员保存一个分数,适合排行榜和按权重排序:

ZADD model:ranking 95 qwen 92 llama 90 mistral
ZSCORE model:ranking qwen
ZRANK model:ranking llama
ZRANGE model:ranking 0 -1 WITHSCORES
ZREVRANGE model:ranking 0 -1 WITHSCORES
ZRANGEBYSCORE model:ranking 90 100 WITHSCORES

7. 使用注意事项

  • Redis 主要依赖内存,应设置合理的过期时间和内存淘汰策略。
  • 不要把 Redis 当作无需设计的数据仓库;是否持久化需要结合业务决定。
  • 缓存中不应存放明文密码、访问令牌等敏感信息。
  • Key 应使用统一命名规则,例如 业务:对象:标识

第三部分:NumPy

NumPy 主要用于数组和数值计算。

1. 创建数组

import numpy as np

one_dimension = np.array([1, 2, 3])
two_dimensions = np.array([[1, 2], [3, 4]])

zeros = np.zeros((2, 3))
ones = np.ones((2, 3))

sequence = np.arange(0, 10, 3)
points = np.linspace(1, 2, 3)

uniform_random = np.random.rand(2, 3)
normal_random = np.random.randn(2, 3)

np.arange() 的第三个参数是步长;np.linspace() 的第三个参数是采样点数量。

2. 数组属性

array = np.array([[1, 2, 3], [4, 5, 6]])

array.ndim   # 维度数量
array.shape  # 形状
array.size   # 元素总数
array.dtype  # 元素类型

3. 调整形状

array.reshape(3, 2)
array.T
array.flatten()
np.resize(array, (3, 4))

reshape() 通常要求新形状与原数组的元素数量一致;resize() 可以改变元素数量,并可能重复或截断数据。

4. 数组计算与矩阵乘法

a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])

a + b
a - b
a * b       # 对应元素相乘
a / b
a ** 2
a @ b       # 矩阵乘法
np.matmul(a, b)
np.dot(a, b)

矩阵 A @ B 要求 A 的列数等于 B 的行数,结果形状为 A 的行数乘 B 的列数。

5. 统计计算

np.sum(array)
np.sum(array, axis=0)  # 按列聚合
np.sum(array, axis=1)  # 按行聚合

np.mean(array)
np.var(array)
np.std(array)

np.max(array)
np.min(array)
np.argmax(array)
np.argmin(array)

6. 广播机制

matrix = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
])

row = np.array([10, 20, 30])
column = np.array([[10], [20], [30]])

print(matrix + row)
print(matrix + column)

广播会从形状的末尾向前比较维度。两个维度相等,或者其中一个为 1 时,通常可以广播;否则会报形状不兼容的错误。

第四部分:Pandas

Pandas 主要用于表格数据读取、清洗、筛选、分组和统计。

1. Series 和 DataFrame

import pandas as pd

series_from_list = pd.Series([1, 2, 3, 4, 5])
series_from_dict = pd.Series({"one": 1, "two": 2, "three": 3})

dataframe = pd.DataFrame({
    "姓名": ["张三", "李四", "王五"],
    "年龄": [18, 19, 20],
    "性别": ["男", "男", "女"],
    "部门": ["行政部", "销售部", "销售部"],
})

自定义列名和索引:

ranking = pd.DataFrame(
    [
        ["张三", 18, "男", "销售部"],
        ["李四", 19, "男", "销售部"],
        ["王五", 20, "女", "销售部"],
    ],
    columns=["姓名", "年龄", "性别", "部门"],
    index=["冠军", "亚军", "季军"],
)

2. 查看属性

ranking.shape
ranking.columns
ranking.index
ranking.dtypes
ranking.head(2)
ranking.tail(2)

3. 查询数据

# 查询列
ranking["姓名"]
ranking[["姓名", "年龄"]]

# 按索引名称查询行
ranking.loc["冠军"]
ranking.loc[["冠军", "亚军"]]

# 按位置查询行
ranking.iloc[0]
ranking.iloc[[0, 1]]

# 行列切片
ranking.iloc[0:2, 0:3]

4. 筛选和排序

ranking.query('年龄 > 18 and 部门 == "销售部"')
ranking.sort_values("年龄", ascending=False)
ranking.sort_values(
    ["部门", "年龄"],
    ascending=[True, False],
)

5. 分组与聚合

dataframe.groupby("部门")["年龄"].sum()
dataframe.groupby(["部门", "性别"])["年龄"].sum()
dataframe.groupby("部门")["年龄"].agg(["sum", "mean", "max", "min"])

透视表:

pivot_table = dataframe.pivot_table(
    index="部门",
    values="年龄",
    aggfunc="mean",
)

6. 读取和清洗 CSV

dataframe = pd.read_csv(
    "文件名.csv",
    sep=",",
    encoding="utf-8",
)

# 统计空值和非空值
dataframe.isnull().sum()
dataframe.notnull().sum()

# 删除空值
cleaned = dataframe.dropna()
dataframe.dropna(inplace=True)
dataframe.dropna(axis=1, inplace=True)

# 填充空值
dataframe.fillna(0)

使用平均值填充时,应只选择数值列:

numeric_columns = dataframe.select_dtypes(include="number").columns
dataframe[numeric_columns] = dataframe[numeric_columns].fillna(
    dataframe[numeric_columns].mean()
)

第五部分:Matplotlib

Matplotlib 主要用于数据可视化。

基本流程:

  1. 导入库。
  2. 配置中文字体等显示选项。
  3. 创建画布。
  4. 准备和清洗数据。
  5. 绘图。
  6. 添加图例、标题、坐标说明和网格。
  7. 展示或保存图表。

绘制中、美、日 GDP 变化曲线:

import matplotlib
import matplotlib.pyplot as plt
import pandas as pd

matplotlib.rcParams["font.sans-serif"] = ["SimHei"]
matplotlib.rcParams["axes.unicode_minus"] = False

dataframe = pd.read_csv(
    "1960-2019全球GDP数据.csv",
    sep=",",
    encoding="gbk",
)
dataframe.dropna(inplace=True)

us_data = dataframe[dataframe["country"] == "美国"].set_index("year")
cn_data = dataframe[dataframe["country"] == "中国"].set_index("year")
jp_data = dataframe[dataframe["country"] == "日本"].set_index("year")

plt.figure(figsize=(9, 6))
plt.plot(us_data.index, us_data["GDP"], label="美国", color="blue")
plt.plot(cn_data.index, cn_data["GDP"], label="中国", color="red")
plt.plot(jp_data.index, jp_data["GDP"], label="日本", color="gold")

plt.legend()
plt.title("1960—2019 年全球 GDP 数据")
plt.xlabel("年份")
plt.ylabel("GDP")
plt.grid()
plt.show()

在无图形界面的服务器上,可以使用 plt.savefig("gdp.png") 保存图片,而不是调用 plt.show()

第六部分:综合学习路径

  1. 使用 MySQL 设计表结构、维护约束并完成复杂查询。
  2. 使用 Redis 处理缓存、计数、集合、临时状态和排行榜。
  3. 使用 NumPy 完成数组、矩阵和统计计算。
  4. 使用 Pandas 读取、清洗、筛选、分组和聚合表格数据。
  5. 使用 Matplotlib 将分析结果绘制成图表。

在实际项目中,MySQL 负责持久化结构化数据,Redis 负责高频访问和临时状态,NumPy 与 Pandas 负责计算和整理数据,Matplotlib 负责展示结果。

返回笔记开头 ↑