Navicat可视化MySQL数据库
Navicat内部封装了所有的操作数据库的命令,用户只需要点击操作即可,无需书写sql语句。
navicat能够充当多个数据库的客户端。
具体操作参考百度。
pymysql模块
连接数据库以及sql语句的使用
import pymysql
# 连接数据库
conn = pymysql.connect(
host = \'127.0.0.1\',
port = 3306,
user = \'root\',
password = \'123456\',
database = \'test1\', # 操作的库名
charset = \'utf8\' # 编码不要加 -
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 产生一个游标对象(就是用来执行sql语句)
\'\'\'
cursor=pymysql.cursors.DictCursor将查询结果以字典的形式返回
\'\'\'
sql = \'select * from emp1;\' # sql语句
res = cursor.execute(sql)
# print(res) # execute返回的结果就是sql语句影响的行数,该返回值一般不使用
# *********************************************************
# 获取执行命令的查询结果
# print(cursor.fetchone()) # 只拿执行这条语句的一条结果
# print(cursor.fetchall()) # 拿执行这条语句的所有结果
# print(cursor.fetchmany(2)) # 指定拿多少条结果
# *********************************************************
# 读取数据类似于光标的移动
# print(cursor.fetchone())
# print(cursor.fetchone())
# print(cursor.fetchall())
\'\'\'
结果:
{\'id\': 1, \'name\': \'hp\', \'sex\': \'male\', \'age\': 18, \'dep_id\': 200}
{\'id\': 2, \'name\': \'sj\', \'sex\': \'female\', \'age\': 20, \'dep_id\': 201}
[{\'id\': 3, \'name\': \'mzx\', \'sex\': \'female\', \'age\': 22, \'dep_id\': 202}, {\'id\': 4, \'name\': \'tank\', \'sex\': \'male\', \'age\': 21, \'dep_id\': 203}, {\'id\': 5, \'name\': \'who\', \'sex\': \'male\', \'age\': 24, \'dep_id\': 204}]
\'\'\'
# *********************************************************
# 控制光标的移动
print(cursor.fetchone())
print(cursor.fetchone())
# cursor.scroll(1,\'relative\') # 相对光标当前位置再往后移动一位 ,relative当前位置,所有id=3的数据没有了
cursor.scroll(1,\'absolute\') # 相对光标开头位置往后移动一位,absolute开头位置
print(cursor.fetchall())
\'\'\'
结果:
cursor.scroll(1,\'relative\') 的执行结果
{\'id\': 1, \'name\': \'hp\', \'sex\': \'male\', \'age\': 18, \'dep_id\': 200}
{\'id\': 2, \'name\': \'sj\', \'sex\': \'female\', \'age\': 20, \'dep_id\': 201}
[{\'id\': 4, \'name\': \'tank\', \'sex\': \'male\', \'age\': 21, \'dep_id\': 203}, {\'id\': 5, \'name\': \'who\', \'sex\': \'male\', \'age\': 24, \'dep_id\': 204}]
cursor.scroll(1,\'absolute\') 的执行结果
{\'id\': 1, \'name\': \'hp\', \'sex\': \'male\', \'age\': 18, \'dep_id\': 200}
{\'id\': 2, \'name\': \'sj\', \'sex\': \'female\', \'age\': 20, \'dep_id\': 201}
[{\'id\': 2, \'name\': \'sj\', \'sex\': \'female\', \'age\': 20, \'dep_id\': 201}, {\'id\': 3, \'name\': \'mzx\', \'sex\': \'female\', \'age\': 22, \'dep_id\': 202}, {\'id\': 4, \'name\': \'tank\', \'sex\': \'male\', \'age\': 21, \'dep_id\': 203}, {\'id\': 5, \'name\': \'who\', \'sex\': \'male\', \'age\': 24, \'dep_id\': 204}]
\'\'\'
增删改查
import pymysql
conn = pymysql.connect(
host = \'127.0.0.1\',
port = 3306,
user = \'root\',
password = \'123456\',
database = \'test1\',
charset = \'utf8\',
autocommit = True
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 增
sql = \'insert into user(username,password) values(%s,%s);\'
# rows = cursor.execute(sql,(\'曦曦\',\'123456\')) # 增加一条数据
rows = cursor.executemany(sql,[(\'曦曦\',\'123456\'),(\'木子曦\',\'123456\')]) #增加多条数据
print(rows)
conn.commit() # 二次确认
# 修改
# sql = \'update user set username="hp_1" where id=1;\'
# rows = cursor.execute(sql)
# print(rows)
# conn.commit() # 二次确认
# 删除
# sql = \'delete from user where id=1;\'
# rows = cursor.execute(sql)
# print(rows)
# conn.commit() # 二次确认
# 查
# sql = \'select * from user;\'
# cursor.execute(sql)
# print(cursor.fetchall())
\'\'\'
由上面代码结果可以看出(除查有结果,其余都没有结果)
增删改-涉及到操作数据的修改
1. 需要二次确认 conn.commit()
2. 如果不想二次确认,在连接数据库加入 autocommit = True
\'\'\'
sql注入问题
\'\'\'
利用语法的特性,通过书写特定的语句实现固定的语法
MySQL利用MySQL的注释语法
select * from user where username=\'hp\' -- 516515\' and password=\'\';
或者
select * from user where username=\'xxx\' or 1=1 -- 215535\' and password=\'\';
\'\'\'
在日常生活中很多软件在注册的时候都不能包含特殊的符号,因为怕你构造出特定的语句入侵数据库
# 解决办法:
\'\'\'
铭感的数据不要自己做拼接,交个execute帮你拼接即可
\'\'\'
import pymysql
# 连接数据库
conn = pymysql.connect(
host = \'127.0.0.1\',
port = 3306,
user = \'root\',
password = \'123456\',
database = \'test1\', # 操作的库名
charset = \'utf8\' # 编码不要加 -
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
username = input(\'>>>:\').strip()
password = input(\'>>>:\').strip()
sql = "select * from user where username=%s and password=%s;"
# 不要手动拼接数据,先用%s占位之后将需要拼接的数据直接交个execute方法即可
rows = cursor.execute(sql,(username,password)) # 自动识别sql里面的%s用作后面元组里面的数据替换
#print(sql)
if rows:
print(\'登陆成功\')
print(cursor.fetchone())
else:
print(\'账号或密码错误!\')
视图(基本不使用)
什么是视图
视图就是通过sql语句查询得到的一张虚拟表保存下来,下次可以直接使用。
其实视图也是表。
为什么要用视图
如果频发的操作一张虚拟表(拼表组成的),可以制成视图,以便后续操作。
如何制作视图
# 固定语法
create view 表名 as 虚拟表的查询sql语句
# 具体操作
create view employee as
select * from emp1 inner join dep on emp1.dep_id=dep.id;
注意
\'\'\'
1 创建视图在硬盘上只会有表结构,没有表数据(数据还是来源于原来的表)
2 视图一般只用来查询里面的数据,不要修改。修改可能会影响原来表的数据。
\'\'\'
触发器(了解)
在满足对表数据进行增、删、改的情况下,自动触发的功能
使用触发器可以帮助我们实现监控、日志等。
触发器可以在六种情况下自动触发,增前、增后、删前、删后、改前、改后。
基本语法
create trigger 触发器名字 before/after insert/update/delete on 表名 for each row
begin
sql语句
end
# 具体使用 针对触发器的名字 通常需要做到见名知意
# 针对增(其他类似)
create trigger tri_before_insert_t1 before insert on t1 for each row
begin
sql语句
end
create trigger tri_after_insert_t1 after insert on t1 for each row
begin
sql语句
end
修改MySQL默认的语句结束符
delimiter $$ 将默认的结束符由;改为$$ 只作用于当前窗口
实现触发器
# 模拟错误日志功能
# 创建表
create table cmd (
id int primary key auto_increment,
user char(32),
priv char(10),
cmd char(64),
sub_time datatime,#提交时间
success enum(\'yes\',\'no\')#0代表执行失败
);
create table errlog (
id int primary key auto_increment,
err_cmd char(64),
err_time datatime
);
\'\'\'
cmd表中记录succes字段是no就触发触发器的执行errlog表中的插入数据
NWE指代的是一条条数据对象,NEW.success指的是cmd表中的success字段
\'\'\'
# 创建触发器
# 修改结束符防止if语句错误
delimiter $$
create trigger tri_after_insert_cmd after insert on cmd
for each row
begin
if NEW.success = \'no\' then
insert into errlog(err_cmd,err_time)
values(NEW.cmd,NEW.sub_time);
end if;
end $$
delimiter ;
# 往cmd 表插入数据
# NOW()获取当前时间
insert into cmd(user,priv,cmd,sub_time,success)
values
(\'hp\',\'0010\',\'ls -l /etc\',NOW(),\'yes\'),
(\'hp\',\'0010\',\'cat /etc/passwd\',NOW(),\'no\'),
(\'hp\',\'0010\',\'useradd xxx\',NOW(),\'no\'),
(\'hp\',\'0010\',\'ps aux\',NOW(),\'yes\');
# 删除触发器
drop trigger tri_after_insert_cmd;
事务
事务的概念
什么是事务:
开启一个事务可以包含多条sql语句,这些sql语句要么同时成功,要么同时失败。
事务的作用:
保证对数据的安全性。
事务的四大特性:
\'\'\'
简称ACID
A:原子性
一个事务是一个不可分割的单位,事务中包含的诸多操作
要么同时成功要么同时失败
C:一致性
事务必须是使数据库从一个一致性的状态变到另外一个状态
一致性跟原子性密切相关的
I:隔离性
一个事务的执行不能被其他事务干扰。
一个事务内部的操作及使用到的数据对并发的其他事务是隔离的,并发执行的事务也是互不干扰。
D:持久性(永久性)
一个事务一旦提交成功执行成功,那么它对数据中数据的修改是永久的,接下来的其他操作或故障不应该对其有任何影响。
\'\'\'
使用事务
# 事务相关的关键字
# 1. 开启事务
start transaction;
# 2. 回滚(回到事务执行之前的状态)
rollback;
# 3. 二次确认(确认之后无法回滚)
commit;
\'\'\'模拟转账功能\'\'\'
create table user(
id int primary key auto_increment,
name char(16),
balance int
);
insert into user(name,balance) values
(\'hp\',1000),
(\'sj\',1000),
(\'mzx\',1000);
# 1 开启事务
start transaction;
# 2 书写多条sql语句
update user set balance=900 where name=\'hp\';
update user set balance=1090 where name=\'sj\';
update user set balance=1010 where name=\'mzx\';
# 3 取消或确认(二选一)
rollback;
or
commit;
存储过程
存储过程类似于python中的自定义函数
它内部包含了一系列的可以执行的sql语句,存储过程存放于MySQL服务端中,可以直接通过调用存储过程触发内部SQL语句的执行。
基本使用
create procedure 存储过程的名字()
begin
sql代码
end
#调用
call 存储过程的名字();
三种开发模式
第一种
\'\'\'
应用程序:程序员写代码开发
MySQL:提前编写好存储过程,供应用程序调用
好处:开发效率提升,执行效率提升
缺点:考虑到人为因素、跨部门沟通的问题,后续的存储过程的扩展性差。
\'\'\'
第二种
\'\'\'
应用程序:程序员写代码开发,设计到数据库操作也是自己动手
好处:扩展性高
缺点:开发效率低,编写sql语句太过繁琐,后续还要对SQL语句优化。
\'\'\'
第三种
\'\'\'
应用程序:只写程序代码,不写sql,基于别人写好的操作MySQL的python框架直接调用操作 ORM框架
好处:开发效率比上面两种情况都要高
缺点:语句的扩展性差,可能会出现效率低的问题
\'\'\'
总结:第一种基本不用,一般都是使用第三种,出现效率问题再动手写sql语句。
演示
delimiter $$
create procedure p1(
in m int, # in只进不出 m不能返回出去
in n int,
out res int # out该形参可以返回出去
)
begin
select name from user where id>m and id < n;
set res = 666; # 将res变量修改 用来标识当前的存储过程代码确实执行
end $$
delimiter ;
# 针对形参res 不能直接传数据,应传一个变量名
# 定义变量
set @ret = 10;
# 查看变量对应的值
select @ret;
# 调用
call p1(1,3,@ret)
在pymysql中调用存储过程
import pymysql
# 连接数据库
conn = pymysql.connect(
host = \'127.0.0.1\',
port = 3306,
user = \'root\',
password = \'123456\',
database = \'test1\', # 操作的库名
charset = \'utf8\' # 编码不要加 -
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 调用存储过程
cursor.callproc(\'p1\',(1,3,10))
\'\'\'
传递值的过程
@_p1_0=1
@_p1_1=3
@_p1_2=10
\'\'\'
# print(cursor.fetchall())
cursor.execute(\'select @_p1_2;\')
print(cursor.fetchall())
函数
与存储过程是有区别的,存储过程是自定义函数,这个函数类似于内置函数。
例如:
(\'hp\',\'0010\',\'ls -l /etc\',NOW(),\'yes\')
NOW()就是内置函数
流程控制
# if 判断
delimiter $$
create procedure proc_if()
begin
declare i int default 0;
if i = 1 then
select 1;
elseif i = 2 then
select 2;
else
select 7;
end if;
end $$
delimiter ;
# while循环
delimiter $$
create procedure proc_while()
begin
declare num int;
set num = 0;
where num < 10 do
select
num ;
set num = num + 1;
end while;
索引
索引:就是一种数据结构,类似于书的目录。意味着查询数据的时候先找目录再找数据,从而提升查询速度降低IO操作。
索引在MySQL中也叫" 键 ",是存储引擎用于快速查找记录的一种数据结构。
- primary key
- unique key
- index key
注意:foreign key 不是用来加速查询用的。
上面的三种key ,前面两种除了加快查询速度还具有约束条件,而最后一种index key没有任何约束条件,只是用来加快查询速度。
本质
通过不断的缩小查询数据的范围筛选出最终的结果,同时将随机事件(整体查询)变成顺序事件(先找目录,再找数据)。
有了索引机制,总是用一种固定的方式查找数据。
一张表中可以有多个索引(多个目录)
缺点
\'\'\'
1 当表中有大量数据存在的前提下 创建索引速度会很慢
2 在索引创建完毕之后 对表的查询性能会大幅度的提升,但是写的性能也会大幅度的降低
\'\'\'
索引不要随意的创建
b+树
\'\'\'
只有叶子节点存放的是真实的数据,其他节点存放的是虚拟数据(仅仅是用来指路的)
树的层级越高查询数据所需的步骤越多(树有几层查询的步骤就有几步)
一个磁盘块的存储是有限制的
建议使用id作为索引,是因为占的空间少,一个磁盘存储的数据多从而降低树的高度,减少查询次数
\'\'\'
聚集索引(primary key)
聚集索引指的就是主键
Innodb 只有两个文件,直接减主键存放在idb表中
MyIsam 三个文件,单独将索引存在一个文件中
辅助索引(unique,index)
查询数据的时候不可能一直使用主键,也可能用到其他字段,那么无法使用聚集索引,这个时候,可以根据情况使用辅助索引。
\'\'\'
叶子节点存放的数据对应的主键值
先按照辅助索引拿到数据的主键值,
再去主键的聚集索引查找数据。
\'\'\'
覆盖索引
在辅助索引的叶子节点已经达到所需的数据,这种现象叫做覆盖索引。
# 覆盖索引,给name 设置辅助索引
select name from user where name=\'hp\';
# 非覆盖索引
select balance from user where name = \'hp\';