第九周总结
线程
线程理论
进程
进程其实是资源单位,表示的是一块内存空间
线程
线程是执行单位,表示真正的代码指令
一个进程内部至少含有一个线程
1.一个进程内可以开设多个线程
2.同一个进程内的多个线程数据共享 # 进程间数据隔离,同进程下的线程数据共享
3.创建进程与线程的区别
创建进程的消耗要远大于线程 # 创建一个进程就等于开辟了一块内存空间,线程不用
创建线程使用threading模块下的Thread类
创建线程的两种方式
from threading import Thread
from multiprocessing import Process
import time
# 方式一:函数
def task(name):
print(f'{name} is running')
time.sleep(0.1)
print(f'{name} is over')
t = Thread(target=task, args=('jason',))
t.start()
print('主线程')
# 方式二:类
class MyThread(Thread):
def run(self):
print('run is running')
time.sleep(1)
print('run is over')
obj = MyThread()
obj.start()
print('主线程')
线程的诸多特性
1.join方法 # 主线程等子线程运行结束后再执行
2.同进程内多个线程数据共享
3.current_thread() # 当前线程的的线程变量
4.active_count() # 返回正在运行的线程数量
GIL全局解释锁
1.在CPython解释器中存在全局解释器锁简称GIL
python解释器有很多类型
CPython JPython PyPython (常用的是CPython解释器)
CPython解释器就是用C编写的Python解释器
2.GIL本质也是一把互斥锁 用来阻止同一个进程内多个线程同时执行(重要)
3.GIL的存在是因为CPython解释器中内存管理不是线程安全的(垃圾回收机制)
由于垃圾回收机制的存在使得线程使用的数据可能会被提前回收掉
GIL与普通互斥锁
GIL只能保证同进程内多线程数据不会被垃圾回收机制影响到,不能保证程序内数据的安全
python多线程用处
单个CPU
IO密集型 多线程有优势
计算密集型 多线程有优势
多个CPU
IO密集型 多线程有优势
计算密集型 多进程完胜
死锁现象
from threading import Thread,Lock
import time
mutexA = Lock() # 产生一把锁
mutexB = Lock() # 产生一把锁
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutexA.acquire()
print(f'{self.name}抢到了A锁')
mutexB.acquire()
print(f'{self.name}抢到了B锁')
mutexB.release()
print(f'{self.name}释放了B锁')
mutexA.release()
print(f'{self.name}释放了A锁')
def func2(self):
mutexB.acquire()
print(f'{self.name}抢到了B锁')
time.sleep(1)
mutexA.acquire()
print(f'{self.name}抢到了A锁')
mutexA.release()
print(f'{self.name}释放了A锁')
mutexB.release()
print(f'{self.name}释放了B锁')
for i in range(10):
obj = MyThread()
obj.start()
以上面这段程序为例,创建了十个线程,第一个线程执行完func1以后,A锁B锁都释放了,这时func2执行了抢到了B锁,但是func2停顿了一秒,导致A锁被后面线程的func1抢去了,然后func2结束停顿后要抢A锁,后面抢到了A锁的线程也要抢B锁,偏偏A锁B锁都被抢了,于是程序就在这里尬住了,这个就是死锁现象
信号量
在python并发编程中信号量相当于多把互斥锁(公共厕所)
from threading import Thread, Lock, Semaphore
import time
import random
sp = Semaphore(5) # 一次性产生五把锁
class MyThread(Thread):
def run(self):
sp.acquire()
print(self.name)
time.sleep(random.randint(1, 3))
sp.release()
for i in range(20):
t = MyThread()
t.start()
event事件
event.set()
event.wait() 程序在此停顿,直到程序执行了event.set()才继续执行
进程池与线程池
因为硬件的限制,进程和线程不能无限制的创建
进程池
提前创建好固定数量的进程供后续程序的调用 超出则等待
线程池
提前创建好固定数量的线程供后续程序的调用 超出则等待
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import os
import time
import random
from threading import current_thread
# 1.产生含有固定数量线程的线程池
# pool = ThreadPoolExecutor(10)
pool = ProcessPoolExecutor(5)
def task(n):
print('task is running')
# time.sleep(random.randint(1, 3))
# print('task is over', n, current_thread().name)
# print('task is over', os.getpid())
return '我是task函数的返回值'
def func(*args, **kwargs):
print('from func')
if __name__ == '__main__':
# 2.将任务提交给线程池即可
for i in range(20):
res = pool.submit(task, 123) # 朝线程池提交任务
print(res.result()) # 不能直接获取
pool.submit(task, 123).add_done_callback(func)
协程
进程:资源单位
线程:执行单位
协程:单线程下实现并发(效率极高)
在代码层面欺骗CPU 让CPU觉得我们的代码里面没有IO操作
实际上IO操作被我们自己写的代码检测 一旦有 立刻让代码执行别的
(该技术完全是程序员自己弄出来的 名字也是程序员自己起的)
核心:自己写代码完成切换+保存状态
import time
from gevent import monkey;
monkey.patch_all() # 固定编写 用于检测所有的IO操作(猴子补丁)
from gevent import spawn
def func1():
print('func1 running')
time.sleep(3)
print('func1 over')
def func2():
print('func2 running')
time.sleep(5)
print('func2 over')
if __name__ == '__main__':
start_time = time.time()
# func1()
# func2()
s1 = spawn(func1) # 检测代码 一旦有IO自动切换(执行没有io的操作 变向的等待io结束)
s2 = spawn(func2)
s1.join()
s2.join()
print(time.time() - start_time) # 8.01237154006958 协程 5.015487432479858
MySQL数据库
存取数据的演变史
1.原始阶段:文本文件
存在文件路径不固定,数据格式不统一的问题
2.软件开发目录规范
规定了数据应该保存在db目录下 路径偏向统一
但数据格式还是不统一
3.数据库服务
路径统一,格式统一,操作方式统一
降低了学习成本与难度,提升开发效率
数据库软件应用史
1.单机
数据存储于各个计算机的本地,无法共享
2.网络
数据存储于网络中,可以共享
数据库本质
1.从底层原理的角度看
数据库指的是操作数据的进程
2.从实际应用的角度看
数据库指的是可视化操作界面
数据库软件本质上也是CS架构的程序
数据库分类
数据库可分为关系型数据库和非关系型数据库
1.关系型数据库
拥有固定的表结构
数据之间可以建立数据库层面关系(可直接在数据库中通过关键字段或类型来链接不同的表)
数据库软件:MySQL、Orcale、MariaDB、PostgreSQL、sql server、sqlite、DB2
2.非关系型数据库
没有固定的表结构,数据存储采用k:v键值对的形式
数据之间不能建立数据库层面的关系,只能通过自己编写代码建立逻辑层面的关系
软件:redis、mongoDB、memcache
MySQL简介
1.版本问题
8.0 最新版
5.7 使用频率较高
5.6 学习推荐使用
2.下载流程
1.访问官网 https://www.mysql.com/
2.点击DOWNLOADS
3.点击GPL(见图一)
4.点击community server(见图二)
5.点击Archives(见图三)
6.选择对应版本对应系统下载即可(见图四)
MySQL基本使用
MySQL使用cmd打开,cmd建议使用管理员身份打开
1.切换到mysql的bin目录下先启动服务端
mysqld
2.保持窗口不关闭 重新打开一个新的cmd窗口
3.切换到mysql的bin目录下启动客户端
mysql
'''
直接使用mysql命令默认是游客模式 权限和功能都很少
管理员登录 mysql -u用户名 -p密码
管理员默认没有密码 连续回车即可
'''
系统服务制作
1.先把bin目录添加到环境变量
清空之前打开的cmd窗口 一定要把之前用cmd启动的服务端关闭(ctrl+c)
2.将mysql添加到系统服务中
1.如何查看系统服务
鼠标右键任务栏选择服务
cmd输入services.msc回车
2.以管理员身份打开cmd窗口
mysqld --install
3.首次添加不会自动启动 需要人为操作一下
1.鼠标右键点击启动
2.命令行启动
net start mysql
'''
卸载流程
1.先关闭服务端
net stop mysql
2.移除系统服务
mysqld --remove
'''
密码相关操作
1.修改密码
方式1:mysqladmin
mysqladmin -u用户名 -p原密码 password 新密码
注意事项:不能登陆以后修改,直接在cmd中修改
方式2:直接修改存储用户数据的表
方式3:冷门操作 有些版本可能还不支持 # 登陆以后修改
set password=password('新密码') # 修改当前登录用户的密码
2.忘记密码
方式1:卸载重新装
方式2:把data目录删除 拷贝同桌的目录
方式3:小把戏操作
1.关闭正常的服务端
2.以跳过授权表的方式重启服务端(不校验密码)
3.以管理员身份进入然后修改mysql.user表数据即可
net stop mysql
mysqld --skip-grant-table
mysql -uroot -p
update mysql.user set password=password('123') where Host='localhost' and User='root';
4.关闭服务端 然后以正常方式启动即可
SQL与NoSQL
数据库服务端是可以服务多种类型的客户端
客户端可以是自己开发的 也可以是python代码编写 也可以是java代码编写
SQL
操作关系型数据库的语言
NoSQL
操作非关系型数据库的语言
ps:要想跟数据库交互就必须使用数据库指定的语言
"""
SQL有时候也指代关系型数据库
NoSQL有时候也指代非关系型数据库
"""
数据库重要概念
"""
强调:小白阶段为了更加方便的理解 做了以下比喻 本质其实有一点点的区别
"""
库 就相当于是 文件夹
表 就相当于是 文件夹里面的文件
记录 就相当于是 文件夹里面的文件中的一行行数据
验证
1.查看所有的库名称
show databases;
2.查看所有的表名称
show tables;
3.查看所有的记录
select * from mysql.user;
基于库的SQL语句操作
1.创建库
create database 库名;
2.查看库
show databases; 查看所有的库名称
show create database 库名; 查看指定库信息
3.编辑库
alter database 库名 charset='utf8';
4.删除库
drop database 库名;
基于表的SQL语句操作
操作表之前需要先确定库
create database db1;
切换操作库
use db1;
1.创建表
create table 表名(字段名 字段类型,字段名 字段类型);
2.查看表
show tables; 查看库下所有的表名称
show create table 表名; 查看指定表信息
describe 表名; 查看表结构
desc 表名;
ps:如果想跨库操作其他表 只需要在表名前加库名即可
desc mysql.user;
3.编辑表
alter table 表名 rename 新表名;
4.删除表
drop table 表名;
基于记录的SQL语句操作
1.插入数据
insert into 表名 values(数据值1,数据值2);
2.查询数据
select * from 表名; 查询表中所有的数据
3.编辑数据
update 表名 set 字段名=新数据 where 筛选条件;
4.删除数据
delete from 表名;
delete from 表名 where id=2;
字符编码与配置文件
1.\s 查看MySQL相关信息
当前用户、版本、编码、端口号
ps: MySQL5.6之前的版本编码需要人为统一,之后的版本全部默认统一
如果想要永久修改编码配置需要操作配置文件
2.永久修改编码配置文件
MySQL默认的配置文件是my-default.ini
1.拷贝默认配置文件并重命名为my.ini
2.拷贝下列字符编码配置,粘贴到my.ini中
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
ps:1.utf8mb4能够存储表情,功能更强大
2.utf8与utf-8是有区别的,MySQL中只有utf8
配置文件中关于[mysqld]的配置被修改后,服务端需要重启
数据库引擎
存储引擎
数据库针对数据才去的多种存取方式
查看常见存储引擎的方式
show engines;
我们需要了解的四个存储引擎
MyISAM
MySQL5.5之前默认的存储引擎,存取数据的速度快,但是功能少、安全性低
InnoDB
MySQL5.5之后默认的存储引擎
支持事物、行锁、外键等操作,存取速度较慢但是安全性高
Menory
基于内存存取数据,仅用于临时表数据存取
BlackHole
任何写进去的数据都会立刻丢失
切换存储引擎用 engine=引擎名称; # MySQL默认忽略大小写
create database db2;
use db2;
create table t1(id int) engine=innodb;
create table t2(id int) engine=myisam;
create table t3(id int) engine=memory;
create table t4(id int) engine=blackhole;
创建表的完整语法
create table 表名(字段名 字段类型(数字) 约束条件,
字段名 字段类型(数字) 约束条件
);
1.字段名和字段类型是必须的
2.数字和约束条件是可选的
3.约束条件也可以写多个 空格隔开即可
4.最后一行结尾不能加逗号
严格模式
当我们在使用数据库存储数据的时候 如果数据不符合规范 应该直接报错而不是擅自修改数据 这样会导致数据的失真(没有实际意义)
正常都应该报错 但是我们之前不小心改了配置文件
show variables like '%mode%';
1.临时修改
set session sql_mode='strict_trans_tables';
在当前客户端有效
set global sql_mode='strict_trans_tables';
在当前服务端有效
2.永久修改
直接修改配置文件
字段类型
整型
MySQL数据库中整型的字段类型有多个,但是我们只需要了解其中几个即可
tinyint 1bytes 正负号(占1bit)
smallint 2bytes 正负号(占1bit)
int 4bytes 正负号(占1bit)
bigint 8bytes 正负号(占1bit)
在MySQL数据库中整型数据的存储是默认自带正负号的
我们也可以取消正负号
create table t5(id tinyint unsigned); # unsigned 起到一个取消正负号的作用
浮点型
MySQL数据库中浮点型的字段类型有多个,但是我们只需要了解三中即可
float(40,20) 共存储40位数,小数点后面占20位
double(40,20) 共存储40位数,小数点后面占20位
decimal(40,20) 共存储40位数,小数点后面占20位
这三个浮点型的核心区别在于对数字的精确度不同
float的精确度大概只能到小数点后七八位
double的精确度大概能到小数点后十四位左右
decimal的精确度最高,几乎是给的什么数就存取什么数
字符类型
MySQL中字符类型有两个
char 定长 char(4)固定存储长度为4个字符,超出会报错,不足会以空格填充至四个
varchar 变长 varchar(4)最多存储四个字符,超出报错,不足则有多少存多少
create table t6(name char(4));
create table t7(name varchar(4));
insert into t6 values('j');
insert into t7 values('j');
直接查看表中存储的数据看不出来数据长短,需要查看字段存储的长度使用 char_length()
默认情况下MySQL针对char的存储会自动填充空格和删除空格,如果要取消空格自动删除使用下面语句
set global sql_mode='strict_trans_tables,pad_char_to_full_length';
二者对比
char 整存整取,速度快但是浪费存储空间
varchar 节省存储空间,但是存取数据的速度较char慢
具体的使用需要根据实际情况
数字的含义
字段类型括号中的数字在很多地方都是用来表示限制存储数据的长度
但是在整形中数据不是用来限制存储长度的,而是用来控制展示的长度
create table t8(id int(3)); # 直接写没啥用
insert into t8 values(12345),(1);
create table t9(id int(3) zerofill);
insert into t9 values(12345),(1);
select * from t9; # 12345,001
# 我们以后写整形无需添加数字除非有特殊需要
枚举与集合
枚举 (多选一)
create table t10(name enum('jason','kevin','jack'));
insert into t10 values('tony'); # 报错
insert into t10 values('jason');
insert into t10 values('kevin');
查询出来的结果为jason和kevin
集合(多选多,多选一)
create table t11(name set('jason','kevin','jack'));
insert into t11 values('tony'); # 报错
insert into t11 values('jack');
insert into tii values('jason','kevin')
select * from t11; # jack jason,kevin
日期类型
日期类型的数据一般都是系统自动操作
create table t12(
register_time datetime, # 年月日时分秒
birthday date, # 年月日
study_time time, # 时分秒
work_time year # 年
);
insert into t12 values('2022-11-23 17:17:33','2022-11-23','17:17:51','2022')
字段约束条件
无符号、零填充
unsigned # 无符号
id int unsigned
zerofill # 零填充
id int(3) zerofill # 存入数字不足三位时以0填充至三位
非空
not null
所有字段类型在不加约束条件的情况下默认都可以为空,即null
非空约束条件下不输值和输null都是一样报错,如果输的值为空格或者说只要输入的值不是null就可以
默认值 default
create table t3(id int default 888,
name varchar(16) default '匿名');
insert into t3 values();
insert into t3 values(2,'jason');
唯一值 unique
单列唯一:表示在某个字段下数据值不重复、
create table t4(id int unique,name varchar(16) unique);
联合唯一:某个字段可能有重复,但是多个字段组合起来的结果不重复
create table t5(id int,ip varchar(32),
port int,unique(ip,port));
主键 primary key
1.但从约束层面上看主键相当于not null + unique 非空且唯一
create table t6(id int primary key,name varchar(16));
2.InnoDB存储引擎规定了所有的表都必须有且只有一个主键(主键是组织数据的重要条件并且主键可以加快数据的查询速度)
1.当表中没有主键也没有其他非空切唯一的字段的情况下
InnoDB会采用一个隐藏的字段作为表的主键 隐藏意味着无法使用 基于该表的数据查询只能一行行查找 速度很慢
2.当表中没有主键但是有其他非空且唯一的字段 那么会从上往下将第一个该字段自动升级为主键
create table t7(id int not null unique,name varchar(16) not null unique);
自增 auto_increment
create table t8(id int primary key auto_increment,name varchar(16));
该约束条件不能单独出现 并且一张表中只能出现一次 主要就是配合主键一起使用
'''
自增特性
自增不会因为数据的删除而回退 永远往下自增
如果中间自己设置了一个更大的数,那么后续基于这个数字继续往下自增
如果想重置某张表的主键值 可以使用
truncate t9; 清空表数据并重置主键
'''
外键
1.外键的作用
用于标识数据与数据之间的关系(主要用于不同表之间数据的关联)
为什么需要外键(以员工信息表为例)
员工信息表内可能有 id name age male e_maile phone address department 等字段
如果我们将所有的数据全放在一张表中,首先会造成存取数据的冗余,其次会导致数据的扩展性极差
如果我们将拆分成不同的表格归类整理,会解决上述问题,但是会造成不同的表之间数据没了关系
什么是外键
外键即外键字段
为了将不同的表数据连接起来,我们使用外键字段,就是在某张表中再加一个字段用于标识另一张表中的数据
2.表关系的判断
表关系一共可以分为四种 一对一 一对多 多对多 没有关系
1.一对一
一张表中的一条数据只能对应另一张表中的一条数据,另一张表中的数据也只能对应这张表中的一条数据,就像商品表与商品库存表,一个商品只能有一个库存,一个商品库存也只能有一个商品
2.一对多
这里我们以商品表和供货商表举例
一个商品只能有一个供货商,一个供货商可以提供多个商品
这种一方只能对应一个,另一方能对应多个的关系就是一对多的关系
3.多对多
以书籍和作者为例
一本书可以署名多个作者,一名作者也可以写多本书
这种双方都是多对多的关系就称之为多对多
3.外键字段的建立
1.一对一
外键字段建立在任何一方都可以,但是建议建立在使用频率较高的一方
create table goods(id int primary key auto_increment,name varchar(32),
detail_id int unique,
# 级联更新级联删除,另一张表修改以后主键所在的表自动跟着修改
foreign key(detail_id) references repertory(id)
on update cascade
on delete cascade);
create table repertory(id int primary key auto_increment,name varchar(32),num int);
2.一对多
create table commodity(id int primary key auto_increment,
name varchar(32),
dep_id int,
foreign key(dep_id) references supplier(id)
on update cascade
on delete cascade);
create table supplier(id int primary key auto_increment,
sup_name varchar(32));
3.多对多
多对多比较特殊,它不能直接在表中创建外键字段,需要有第三张表,用这张表来存储那两张表之间的关系
create table book(id int primary key auto_increment,
title varchar(32),price float(5,2));
create table author(id int primary key auto_increment,
name varchar(32));
# 创建第三张表用来存储两张表之间数据的关系
create table book_author(id int primary key auto_increment,
author_id int,
foreign key(author_id) references author(id)
on update cascade
on delete cascade,
book_id int,
foreign key(book_id) references book(id)
on update cascade
on delete cascade);
SQL语句查询关键字
select、from
select
指定需要查询的字段信息
select * 查询所有字段
select name 查询name字段
select char_length(name) 也可以对字段做处理
from
指定需要查询的表信息,就是从哪个表里面查
SQL语句中关键字的执行顺序和编写顺序不是一致的
select * from user; # 先执行from再执行select
学习SQL语句查询关键字前期的数据准备
create table emp(
id int primary key auto_increment,
name varchar(20) not null,
gender enum('male','female') not null default 'male', #大部分是男的
age int(3) unsigned not null default 28,
hire_date date not null,
post varchar(50),
post_comment varchar(100),
salary double(15,2),
office int, #一个部门一个屋子
depart_id int
);
#插入记录
#三个部门:教学,销售,运营
insert into emp(name,gender,age,hire_date,post,salary,office,depart_id) values
('jason','male',18,'20170301','浦东第一帅形象代言',7300.33,401,1), #以下是教学部
('tom','male',78,'20150302','teacher',1000000.31,401,1),
('kevin','male',81,'20130305','teacher',8300,401,1),
('tony','male',73,'20140701','teacher',3500,401,1),
('owen','male',28,'20121101','teacher',2100,401,1),
('jack','female',18,'20110211','teacher',9000,401,1),
('jenny','male',18,'19000301','teacher',30000,401,1),
('sank','male',48,'20101111','teacher',10000,401,1),
('哈哈','female',48,'20150311','sale',3000.13,402,2),#以下是销售部门
('呵呵','female',38,'20101101','sale',2000.35,402,2),
('西西','female',18,'20110312','sale',1000.37,402,2),
('乐乐','female',18,'20160513','sale',3000.29,402,2),
('拉拉','female',28,'20170127','sale',4000.33,402,2),
('僧龙','male',28,'20160311','operation',10000.13,403,3), #以下是运营部门
('程咬金','male',18,'19970312','operation',20000,403,3),
('程咬银','female',18,'20130311','operation',19000,403,3),
('程咬铜','male',18,'20150411','operation',18000,403,3),
('程咬铁','female',18,'20140512','operation',17000,403,3);
where 筛选
1.逻辑运算
# 查询id大于等于3小于等于6的数据
select * from emp where id >= 3 and id <= 6;
这样写比较繁琐,可以简化成
select * from emp where id between 3 and 6;
2.成员运算
select * from emp where salary in (20000,18000,17000);
3.模糊查询
模糊查询即条件不够精确的查询,关键字为like
模糊查询常用的符号
%:匹配任意个数的任意字符
eg:
%o% 查询任意位置有o的数据
%o 查询结尾为o的数据
o% 查询开头为o的数据
_:匹配单个个数的任意字符
eg:
_o_ 匹配三个字符且中间为o的数据
_o 匹配两个字符且结尾为o的数据
# 查询员工姓名中包含字母o的员工姓名与薪资
select * from emp where name like '%o%';
4.根据数据长度查询
# 查询员工姓名是由四个字符组成的员工姓名与其薪资
select name,salary from emp where name like '____'; # 直接使用模糊查询
select name,salary from emp where char_length(name) = 4; # 使用关键字char_length
5.查询数据值为空的数据
针对null不能使用等号,使用等号会报错,只能使用is
# 查询岗位描述为空的员工名与岗位名
select name,post from emp where post_comment is null;
group by 分组
分组就是按照指定条件将单个数据组成一个整体,这样可以更好地统计相关数据
关于分组后的数据有聚合函数可以专门用于处理这个
max\min\sum\avg\count 最大值\最小值\求和\平均数\计数
分组之后默认的最小单位就应该是组 而不应该再是组内的单个数据单个字段
分组以外的字段无法直接填写,需要借助方法group_concat
select post,group_concat(name) from emp group by post; # 结果是按照分组聚合在一起的,数据之间以逗号隔开
select post,group_concat(name,age) from emp group by post;
# group_concat括号内的字段查询出的结果是直接拼在一起的,可以在中间加符号将其分隔开
1.获取最大数据值
select post,max(salary) from emp group by post;
针对sql语句执行之后的结果,其字段名可以被修改,使用关键字as,也可以省略不写
select post '部门',max(salary) '最高薪资' from emp group by post;
2.统计个数
select post,count(id) from emp group by post;
having 过滤
having与where在本质上都是一样的,都是用来对数据进行筛选的
只是where用在分组之前(第一次筛选),having用在分组之后(二次筛选)
# 统计各部门年龄在30岁以上的员工平均工资 并且保留大于10000的数据
1.先筛选出所有年龄大于三十岁的员工
select * from emp where age > 30;
2.对筛选出来的数据按照部门进行分组,并计算平均薪资
select post,avg(salary) from emp where age > 30 group by post;
3.对分组统计之后的数据做二次筛选
select post,avg(salary) from emp where age > 30 group by post having avg(salary) > 10000;
distinct 去重
去重的前提条件是数据必须一模一样才可以去重
select distinct age from emp; # 针对单个字段
select distinct age,office from emp; # 针对多个字段组合起来的结果
order by 排序
1.单个字段排序
select * from emp order by age; # 默认升序
select * from emp order by age asc; # 升序的完整写法(asc可以省略)
select * from emp order by age desc; # 降序
2.多个字段排序
select * from emp order by age,salary desc; # 先按照年龄升序拍,相同的情况下再按照薪资的降序拍
# 统计各部门年龄在30岁以上的员工平均工资,并且保留平均工资大于1000的部门,然后对平均工资进行排序
1.首先筛选出所有年龄大于30的员工
select * from emp where age > 30;
2.再对他们按照部门分组统计平均薪资
select post,avg(salary) from emp where age > 10 group by post;
3.针对分组的结果做二次筛选
select post,avg(salary) from emp where age > 10 group by post having avg(salary)>1000;
4.最后按照指定字段排序
select post,avg(salary) from emp where age > 10 group by post having avg(salary)>1000 order by avg(salary);
limit 分页
select * from emp limit 5; # 直接限制展示的条数
select * from emp limit 5,4; # 从第五条开始往后读取四条
regexp 正则表达式
select * from emp where name regexp '^j.*(n|y)$';
多表查询的思路
1.表数据准备
create table dep(
id int primary key auto_increment,
name varchar(20)
);
create table emp1(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') not null default 'male',
age int,
dep_id int
);
#插入数据
insert into dep values
(200,'技术'),
(201,'人力资源'),
(202,'销售'),
(203,'运营'),
(205,'财务');
insert into emp1(name,sex,age,dep_id) values
('jason','male',18,200),
('dragon','female',48,201),
('kevin','male',18,201),
('nick','male',28,202),
('owen','male',18,203),
('jerry','female',18,204);
select * from emp1,dep; # 将两张表中所有数据对应一遍 结果见图一
这个现象我们也称之为'笛卡尔积' 无脑的对应没有意义 应该将有关系的数据对应到一起才合理
基于笛卡尔积可以将部门编号与部门id相同的数据筛选出来
涉及到两张及以上的表时 字段很容易冲突 我们需要在字段前面加上表名来指定
select * from emp1,dep where emp1.dep_id=dep.id; # 结果见图二
基于上述的操作就可以将多张表合并到一起然后一次性获取更多的数据
标签:总结,name,int,create,线程,第九,id,select
From: https://www.cnblogs.com/zyg111/p/16930265.html