MySQL
MySQL
数据库
数据库是以一定方式储存在一起、能与多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合,可视为电子化的文件柜——存储电子文件的处所,用户可以对文件中的数据进行新增、查询、更新、删除等操作。
数据库分类
-
关系型数据库
关系型数据库和常见的表格比较相似,数据表与数据表之间是有很多复杂的关联关系的
大多数的关系型数据库都遵循SQL(结构化查询语言,Structured Query Language)
常见的关系型数据库有:MySQL,SQLserver
-
非关系型数据库
出于简化数据库结构、避免冗余、影响性能的表连接、摒弃复杂分布式的目的被设计。
指的是分布式的、非关系型的、不保证遵循ACID(数据库事务正确执行的四个基本要素的缩写)原则的数据存储系统。
通常以数据集的方式,大量的数据集中存储在一起,类似于键值对、图结构或者文档。
常见的非关系型数据库有:Redis、MongoDB
MySQL数据库的安装与卸载
-
安装
个人推荐使用安装包安装的形式安装MySQL数据库。省时省力,但是希望你们别遇到像我一样的数据库编码问题(my.ini配置了没用)。如果你们也遇到了数据库编码问题的话,解压包形式的数据库应该能够解决你们的问题。
- 解压免安装版本(没有初始密码)
- 配置环境变量:配置数据库绝对路径下的bin目录到环境变量中
- 配置服务:启动管理员终端(win+x打开Windows PowerShell(管理员))输入命令:mysqld --install
-
卸载
如果是安装版的话会比较麻烦:
- 先按照正常软件一样卸载MySQL
- 删除C:\Program Files\MySQL与C:\ProgramData\MySQL文件夹(ProgramData默认是隐藏的)
- 将注册表中的关于MySQL的项全部删除
连接数据库
-
在cmd中使用mysql命令连接数据库,单纯使用mysql是以游客模式登陆,没有大部分功能。
-
mysql -u root -p
免安装版初始没有密码,直接回车。 -
使用-h 指定远程MySQL ip地址,使用-P指定端口号(MySQL默认使用3306端口)
mysql -h 127.0.0.1 -P 2206 -u root -p
-
退出数据库使用 exit 或 quit
用户
-
使用
select user();
来查看当前登陆用户 -
所有的用户信息都在 mysql 数据库的user数据表中,使用
select * from mysql.user
查看所有用户信息 -
创建用户
grant 权限 on 数据库.数据表 to 用户名@'主机名' identified by '密码'; # 如果账户不存在则创建该账户
-
撤销权限
revoke 权限 on 数据库.数据表 from 用户名@'主机名';
-
删除用户
drop user 用户名@'主机名';
修改密码
-
登陆状态下:
update mysql.user set password = password(新密码) where user != '';
-
cmd命令:
mysqladmin -u root -p旧密码 -h域名 password 新密码
数据库编码
修改数据库编码是我的一大痛处
这个可以去看看别人的数据库编码怎么修改,没有什么成功的经验,这几次都是运气修改掉了。气
数据库基本操作
-
查看已存在数据库
show databases;
-
创建数据库
create databases 数据库名 charset=编码; # 如果不设置数据库编码就使用默认编码
-
查看数据库的详细信息
show create database 数据库名;
-
选择数据库
use 数据库名;
-
查看数据库中的所有数据表
show tables;
-
创建数据表
create table 表名( 字段1 数据类型 约束 ... 字段n 数据类型 约束 )charset = 编码 engine = innoDB; # 一般引擎默认是innoDB
-
查看数据表详细信息
show create table 数据表名;
-
查看数据表结构
desc 数据表;
-
查看数据
select * from 表名; # * 代表显示所有字段信息 # 可以将 * 替换成字段,指定字段信息显示
-
插入数据
insert into 表名(字段1,...字段n) values(字段值1,...字段值n); insert into 表名 values(按照字段顺序填写);
-
更新数据
update 表名 set 字段 = 字段值 where 字段 = 字段值; # 如果没有where后的语句会修改整个表的数据
-
删除数据
delete from 表名 where 字段 = 字段值; # 如果没有where后的语句会删除整个表的数据
-
删除数据表
drop table 表名;
-
删除数据库(谨慎)
drop database 数据库名;
数据库表的修改
-
修改数据库编码
alter database 数据库名 charset=编码;
-
修改数据表名字
alter table 旧表名 rename 新表名;
-
修改数据表字段名
alter table 表名 change 旧字段 新字段 数据类型;
-
修改数据表字段数据类型
alter table 表名 modify 字段 新类型;
-
增加数据库表字段
alter table 表名 add 字段名 类型;
-
删除数据库表字段
alter table 表名 drop 字段名;
MySQL支持的数据类型
-
整型
tinyint 1字节 -127~127 smallint 2字节 -32768~32767 mediumit 3字节 int 4字节 -2147483648~2147483647 bigint 8字节
-
浮点型
float(M, D):4字节,3.4E–38~3.4E+38 double(M, D):8字节,1.7E–308~1.7E+308 decimal(M, D):所在字节M,D大值基础上+2,其实就是M值+2就是decimal字段所占字节数 (M, D) => M为位数,D为小数位,M要大于等于D float(255, 30):精度最低,最常用 double(255, 30):精度高,占位多 decimal(65, 30):字符串存,全精度
-
字符串型
char:定长,永远采用设置的长度存储数据 varchar:不定长,在设置的长度范围内,变长的存储数据
-
时间
year:yyyy(1901/2155) date:yyyy-MM-dd(1000-01-01/9999-12-31) time:HH:mm:ss datetime:yyyy-MM-dd HH:mm:ss(1000-01-01 00:00:00/9999-12-31 23:59:59) timestamp:yyyy-MM-dd HH:mm:ss(1970-01-01 00:00:00/2038-01-19)
-
枚举与集合
为某一个字段提供选项的 - 枚举只能单选(1个),集合可以多选(0-n个)
约束
primary key:主键,唯一标识,表都会拥有,不设置为默认找第一个 不空,唯一 字段,未标识则创建隐藏字段
foreign key:外键
unique:唯一性数据, 该条字段的值需要保证唯一,不能重复
auto_increment:自增,只能加给key的int类型字段,作为辅助修饰,一个表中只能设置一个自增字段
not null:设置字段不为空 - 针对一些字段
default:默认值 - 对有默认值意外的字段进行赋值时,有默认值的字段会被赋默认值
unsigned:无符号 - 存储的数字从0开始对整型使用
zerofill:0填充 - 存整数时数据长度小于取值范围长度,会在数字左方用0填充
多表间关系
- 一对一
- 一对多
- 多对多
外键
-
外键的 字段名 可以自定义(名字随意),通常命名规范(关联表_关联字段)
-
外键要通过 foreign key 语法建立表与表之间的关联
-
foreign key(所在表的外键字段) references 关联表(关联字段)
-
级联关系
级联更新 on update cascade
级联删除 on delete cascade
单表操作
-
增:
insert into 表名 values (数据1, ... , 数据n); -
删:
delete from 表名 条件; -
改:
update 表名 set 字段1=值1, ..., 字段n=值n 条件; -
查:
select * from 表名 条件;
函数
拼接:
- concat() :concat(字段,连接符,字段,连接符,...,字段)
- concat_ws():concat_ws(连接符,字段,...,字段)
大小写:
- upper() :字段
- lower():字段
浮点型操作:
- ceil() :上取整
- floor() :下取整
- round():四舍五入
整型:可以直接运算
聚合函数
- max():最大值
- min():最小值
- avg():平均值
- sum():求和
- count():记数
- group_concat():组内字段拼接,用来查看组内其他字段
查询条件
from、where、group by、having、distinct、order by、limit 遵循层层筛选的结果(distinct书写位置特殊)
数据准备
CREATE TABLE `emp` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`name` varchar(10) NOT NULL,
`gender` enum('男','女') NULL DEFAULT '男',
`age` int(0) NULL DEFAULT 0,
`salary` float NULL DEFAULT 0,
`area` varchar(20) NULL DEFAULT '中国',
`port` varchar(20) DEFAULT '未知',
PRIMARY KEY (`id`)
);
INSERT INTO `emp` VALUES
(1, 'yang', '男', 42, 10.5, '上海', '浦东'),
(2, 'grede', '男', 38, 9.4, '山东', '济南'),
(3, 'jerry', '女', 30, 3.0, '江苏', '张家港'),
(4, 'ljk', '女', 28, 2.4, '广州', '广东'),
(5, 'lik', '男', 28, 2.4, '江苏', '苏州'),
(6, 'sjd', '男', 18, 8.8, '中国', '黄浦'),
(7, 'dgq', '男', 18, 8.8, '安徽', '宣城'),
(8, 'nck', '男', 28, 9.8, '安徽', '巢湖'),
(9, 'sdj', '女', 36, 1.2, '安徽', '芜湖'),
(10, 'hs', '男', 36, 5.8, '山东', '济南'),
(11, 'ybx', '女', 28, 1.2, '山东', '青岛'),
(12, 'san', '男', 30, 9.0, '上海', '浦东'),
(13, 'san1', '男', 30, 6.0, '上海', '浦东'),
(14, 'san2', '男', 30, 6.0, '上海', '浦西'),
(15, 'cpf', '女', 67, 2.501, '上海', '陆家嘴');
去重:distinct
select * from t1; # 全部结果
select distinct * from t1; # 去重后结果,去重是指查询出来的记录字段值都相同时判断是同一条数据
条件:where
select * from t1 where id > 10;
分组:group by
select * from emp group by dep; # 以dep分组
联合分组
select * from emp group by area,port;
筛选:having
where和having的区别:
- 表象:在没有分组的情况下与where条件没有区别
- 内在:having 可以对聚合结果进行筛选
select *, min(salary) from emp group by dep having min(salary)<2;
排序:order
select * from emp order by age asc; # 升序(默认)
select * from emp order by salary desc; # 降序
限制:limit
-
limit 显示条数
select * from emp limit 5;
-
limit 偏移量,显示条数
select * from emp limit 3,5;
连表查询
-
内连接
保留两表都有对应关系的
select 字段 from A表 inner join B表 on A表.关联字段=B表.关联字段;
-
左连接
保留左表的数据
select 字段 from 左表 left join 右表 on 左表.关联字段=右表.关联字段
-
右连接
保留右表的数据
select 字段 from 左表 right join 右表 on 左表.关联字段=右表.关联字段
-
全连接
保留两表的数据
select 字段 from 左表 left join 右表 on 左表.关联字段=右表.关联字段 union select 字段 from 左表 right join 右表 on 左表.关联字段=右表.关联字段
子查询
# 增:insert into 表 select子查询
# 删:delete from 表 条件是select子查询(表不能与delete表相同)
# 查:select 字段 from 表 条件是select子查询
# 改:update 表 set 字段=值 条件是select子查询(表不能与update表相同)
# 子查询 - 查
select * from emp where (salary) in (select max(salary) from emp group by area);
# 将子查询转换为一张表——创建表
create table t1(area varchar(64), max_salary decimal(5,2));
# 子查询 - 增
insert into t1 select max(salary) from emp group by area;
# 子查询 - 改
update t1 set max_salary=max_salary-1 where name,salary in (select name,max(salary) from emp);
# 子查询 - 删
delete from t1 where name,max_salary in (select name, max(salary) from emp);
all与any:区间修饰条件
# 语法规则
where id in (1, 2, 3) # id是1或2或3
where id not in (1, 2, 3) # id不是1,2,3
where salary < all(3, 6, 9) # salary必须小于所有情况(小于最小)
where salary > all(3, 6, 9) # salary必须大于所有情况(大于最大)
where salary < any(3, 6, 9) # salary只要小于一种情况(小于最大)
where salary > any(3, 6, 9) # salary只要大于一种情况(大于最小)
视图
# create view 视图名 as select 语句;
create view v1 as select dep, max(salary) from emp group by area;
# 创建或替换视图
alter 视图名[(别名们)] as select 语句;
create or replace view v1(area, max_salary) as select area, max(salary) from emp group by area;
alter view v1(name, salary) as select area, max(salary) from emp group by area;
# 删除视图
mysql>: drop view 视图名
eg>: drop view v1;
# 视图可以作为正常表完成连表查询
select name, area, salary
from emp join v1
on emp.area=v1.name and emp.salary=v1.max_salary;
视图的增删改
create or replace view v2 as select id,name,age,salary from emp;
update v2 set salary=salary+1 where id=1;
delete from v2 where id=1;
create or replace view v3 as select * from emp;
insert into v3 values(1, 'yangsir', '男', 66, 1.11, '上海', '那噶的');
事务
# 事务:通常一些业务需要多条sql参与,参与的sql会形参一个执行整体,该整体我们就称之为——事务
# 简而言之:事务就是保护多条sql执行的sql语句
""" 事务的四大特性(ACID)
1.原子性:事务是一组不可分割的单位,要么同时成功,要么同时不成功
2.一致性:事物前后的数据完整性应该保持一致(数据库的完整性:如果数据库在某一时间点下,所有的数据都符合所有的约束,则称数据库为完整性的状态)
3.隔离性:事物的隔离性是指多个用户并发访问数据时,一个用户的事物不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离
4.持久性:持久性是指一个事物一旦被提交,它对数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
"""
# mysql中事务的执行
create table bank(
id int,
name varchar(16),
money decimal(65, 2)
);
insert into bank values(1, '张三', 10), (2, "李四", 10);
# 假设出现以下执行情况
# 没有事务支持情况下,Tom的钱就丢了
update bank set money=money-1 where name='张三';
update bank set money=money+1 where name='王五';
# 将两条sql看做事务处理
# 开启事务
begin;
update bank set money=money-1 where name='Tom';
update bank set money=money+1 where name='王五';
# 确认无误,提交事务
commit;
# 确认有误,回滚
rollback;
PyCharm操作MySQL
安装
pip install pymysql
数据库的连接
# pymysql连接数据库的必要参数:主机、端口、用户名、密码、数据库
# 注:pymysql不能提供创建数据库的服务,数据库要提前创建
import pymysql
# 1)建立数据库连接对象 conn
# 2)通过 conn 创建操作sql的 游标对象
# 3)编写sql交给 cursor 执行
# 4)如果是查询,通过 cursor对象 获取结果
# 5)操作完毕,端口操作与连接
创建表
import pymysql
conn = pymysql.connect(user='root', passwd='123456', database='lx', host='localhost', port=3306, autocommit = True)
cursor = conn.cursor(pymysql.cursors.DictCursor)
sql = 'create table t1(id int, x int, y int)'
res = cousor.execute(sql)
cursor.close()
conn.close()
增
import pymysql
conn = pymysql.connect(user='root', passwd='123456', database='lx', host='localhost', port=3306, autocommit = True)
cursor = conn.cursor(pymysql.cursors.DictCursor)
sql = 'insert into t1 values(null, %s, %s)'
res = cursor.execute(sql, [10, 200])
cursor.close()
conn.close()
删
import pymysql
conn = pymysql.connect(user='root', passwd='123456', database='lx', host='localhost', port=3306, autocommit = True)
cursor = conn.cursor(pymysql.cursors.DictCursor)
sql = 'delete from t1 where id=%s'
cursor.execute(sql, 1)
cursor.close()
conn.close()
改
import pymysql
conn = pymysql.connect(user='root', passwd='123456', database='lx', host='localhost', port=3306, autocommit = True)
cursor = conn.cursor(pymysql.cursors.DictCursor)
sql = 'update t1 set y=666 where id=2'
cursor.execute(sql)
cursor.close()
conn.close()
查
import pymysql
conn = pymysql.connect(user='root', passwd='123456', database='lx', host='localhost', port=3306, autocommit = True)
cursor = conn.cursor(pymysql.cursors.DictCursor)
sql = 'select * from t1'
row = cursor.execute(sql) # 返回值是受影响的行
# 通过cursor获取数据
r1 = cursor.fetchone() # 取出一条
r2 = cursor.fetchmany(1) # fetchone() 偏移一条取出,fetchmany(n) 偏移n条取出
r3 = cursor.fetchall() # fetchall() 取出全部
游标操作
import pymysql
from pymysql.cursors import DictCursor
# 1)建立数据库连接对象 conn
conn = pymysql.connect(user='root', passwd='123456', database='lx', host='localhost', port=3306, autocommit = True)
# 2)通过 conn 创建操作sql的 游标对象
cursor = conn.cursor(DictCursor)
# 3)编写sql交给 cursor 执行
sql = 'select * from t1'
# 4)如果是查询,通过cursor对象获取结果
row = cursor.execute(sql)
if row:
r1 = cursor.fetchmany(2)
# 操作游标
cursor.scroll(0, 'absolute') # absolute绝对偏移,游标重置,从头开始偏移
cursor.scroll(-2, 'relative') # relative相对偏移,游标在当前位置进行左右偏移
# 5)操作完毕,关闭游标与连接
cursor.close()
conn.close()
pymysql事务
import pymysql
from pymysql.cursors import DictCursor
conn = pymysql.connect(user='root', passwd='123456', database='lx', host='localhost', port=3306)
cursor = conn.cursor(DictCursor)
try:
sql = 'create table t2(id int, name char(4), money int)'
row = cursor.execute(sql)
except:
print('表已创建')
# 空表才插入
row = cursor.execute('select * from t2')
if not row:
sql = 'insert into t2 values(%s,%s,%s)'
row = cursor.executemany(sql, [(1, '张三', 10), (2, '李四', 10)])
try:
sql1 = 'update t2 set money=money-1 where name="张三"'
r1 = cursor.execute(sql1)
sql2 = 'update t2 set money=money+1 where name="王五"' # 转入的人不存在
r2 = cursor.execute(sql2)
except:
print('转账执行异常')
conn.rollback()
else:
# 当两个都由值时
if r1 == 1 and r2 == 1:
print('转账成功')
conn.commit()
else:
conn.rollback()
SQL注入
import pymysql
from pymysql.cursors import DictCursor
conn = pymysql.connect(user='root', passwd='123456', database='lx', host='localhost', port=3306)
cursor = conn.cursor(DictCursor)
try:
sql = 'create table user(id int, name char(4), password char(6))'
row = cursor.execute(sql)
except:
print('表已创建')
# 空表才插入
row = cursor.execute('select * from user')
if not row:
sql = 'insert into user values(%s,%s,%s)'
row = cursor.executemany(sql, [(1, 'tom', '123'), (2, 'bob', 'abc')])
conn.commit()
# 自己拼接参数一定有sql注入,将数据的占位填充交给pymysql
sql = 'select * from user where name=%s and password=%s'
row = cursor.execute(sql, [usr, pwd])
if row:
print('登录成功')
else:
print('登录失败')
# sql注入
# 输入已知用户时:
# tom" # => select * from user where name="tom" #" and password="%s"
# 不自定义用户名时
# " or 1=1 # => select * from user where name="" or 1=1 #" and password="%s"
索引
# 索引就是 键 - key
"""
1)键 是添加给数据库表的 字段 的
2)给表创建 键 后,该表不仅会形参 表结构、表数据,还有 键的B+结构图
3)键的结构图是需要维护的,在数据完成增、删、改操作时,只要影响到有键的字段,结构图都要维护一次
所以创建键后一定会降低 增、删、改 的效率
4)键可以极大的加快查询速度(开发需求中,几乎业务都和查有关系)
5)建立键的方式:主键、外键、唯一键、index
"""
import pymysql
from pymysql.cursors import DictCursor
conn = pymysql.connect(user='root', passwd='root', db='oldboy')
cursor = conn.cursor(DictCursor)
# 创建两张表
# sql1 = """create table a1(
# id int primary key auto_increment,
# x int,
# y int
# )"""
# cursor.execute(sql1)
# sql2 = """create table a2(
# id int primary key auto_increment,
# x int,
# y int,
# index(x)
# )"""
# cursor.execute(sql2)
# 每个表插入5000条数据
# import random
# for i in range(1, 5001):
# x = i
# y = random.randint(1, 5000)
# cursor.execute('insert into a1(x, y) values(%s, %s)', (x, y))
# cursor.execute('insert into a2(x, y) values(%s, %s)', (x, y))
#
# conn.commit()
import time
# a1的x、a1的id、a2的x
b_time = time.time()
sql = 'select * from a1 where id=4975'
cursor.execute(sql)
e_time = time.time()
print(e_time - b_time)
b_time = time.time()
sql = 'select * from a1 where x=4975'
cursor.execute(sql)
e_time = time.time()
print(e_time - b_time)
b_time = time.time()
sql = 'select * from a2 where x=4975'
cursor.execute(sql)
e_time = time.time()
print(e_time - b_time)