RBHGO的主页欢迎关注
温馨提示:创作不易,如有转载,注明出处,感谢配合~
DCL —> 授予或者召回用户权限 —> grant / revoke
在前两篇分享中,依次叙述了SQL的DDL (数据定义语言)、DML (数据操作语言)以及DQL (数据查询语言),这一篇就先与大家分享最后一种DCL。大家可以想一想,我们构建一个完整的数据库,肯定不是让自己一个人使用吧,开发人员要使用,数据分析人员要使用,前端人员可能要使用,超级管理员也要使用;所以不可能所有人都用管理员账号登入数据库,那还不乱了套了。虽然我们可以在图形化工具中手动配置,可每个数据库的操作不一样;但是SQL语句接入是一样的,所以如果您要干这一行还是好好学习了解所有的SQL语句,当然重点是学习自己的专业方向的SQL。文章内用到的数据库均来自RBHGO的学习日志16
接下来就和大家分享DCL
-- 创建用户(可以远程登录的账号并为其指定口令)---> @号前面是账户名,后面是IP地址
-- IP地址可以用详细的,也可用%表示模糊,因为大部分时候同一时间会有多个guest账户同时连入数据库
-- 例如 % / 114.120.138.% / 127.0.0.1
create user 'guest'@'%' identified by '123456789';
-- 删除用户
drop user 'guest'@'%';
-- 修改密码(将名为guest的账号密码改为guest.123)
alter user 'guest'@'%' identified by 'guest.123';
-- 授权
-- 授予guest对hrs库的操作权限 database_name.table_name (数据库名 英文的点 二维表名)
-- 例如这里的 hrs.* ---> 表示hrs库中的所有的表
grant select,insert, delete, update on hrs.* to 'guest'@'%';
-- 全授权
-- 授权,除了不能授予别人这一项
grant all privileges on *.* to 'guest'@'%';
-- 全授权
grant all privileges on *.* to 'guest'@'%' with grant option;
-- 召回权限(召回guest对hrs数据库所有对象的select/insert/delete/update权限)
revoke select,insert, delete, update on hrs.* from 'guest'@'%';
-- 让权限立马生效
flush privileges;
在Python 3中,我们通常使用纯Python的三方库PyMySQL
来访问MySQL数据库,它应该是目前Python操作MySQL数据库最好的选择。
安装PyMySQL
。
在终端或者说Terminal中输入
pip install pymysql
下载完成后,在项目文件中
import pymysql
重点提示:
因为存在SQL注入攻击,在python代码中写sql语句,不能使用任何形式的格式化(拼接)字符串
"""
Python连接 MySQL,插入数据
安装三方库:
- pymysql ---> 纯python编写,安装一定可以成功
- mysqlclient ---> 底层用C编写,安装有可能无法成功
事务:把若干个(增删改)操作视为一个不可分割的原子性操作,要么全部成功,要么全部失败
- 如果操作成功,我们可以通过连接对象的commit方法手动提交
- 如果操作失败,我们可以通过连接对象的rollback方法回滚,删除操作
Author: RBHGO
Declaration: Mia San Mia ~~~
"""
import pymysql
no = input('请输入部门编号:')
name = input('请输入部门名称:')
area = input('请输入部门地址:')
# 第一步:创建连接
# 在默认情况下创建连接,相当于开启了一个事物环境
# 事务:把若干个(增删改)操作视为一个不可分割的原子性操作,要么全部成功,要么全部失败
# - 如果操作成功,我们可以通过连接对象的commit方法手动提交
# - 如果操作失败,我们可以通过连接对象的rollback方法实现事务回滚,删除操作
conn = pymysql.connect(host='localhost', port=3306,
user='您设的用户名', password='您的密码',
database='hrs', charset='utf8mb4')
# 默认是手动提交,想要自动 connect()加上参数autocommit=True
try:
# 第二步:获取游标对象
with conn.cursor() as cursor:
# 第三步:通过游标对象向数据库发出SQL语句获得执行结果(sql用%s做占位符,后面用元组输入内容)
affected_rows = cursor.execute(
'insert into tb_dept values(%s, %s, %s)',
(no, name, area))
print(affected_rows)
# 第四步成功:手动提交
conn.commit()
except pymysql.MySQLError:
# 第四步失败:手动回滚
conn.rollback()
finally:
# 第五步:关闭连接释放资源
conn.close()
"""
Python连接 MySQL,删除数据
Author: RBHGO
"""
import pymysql
no = input('请输入要删除的部门编号:')
conn = pymysql.connect(host='localhost', port=3306,
user='您设的用户名', password='您的密码',
database='hrs', charset='utf8mb4')
try:
# 第二步:获取游标对象
with conn.cursor() as cursor:
# 第三步:通过游标对象向数据库发出SQL语句获得执行结果
affected_rows = cursor.execute(
'delete from tb_dept where dno=%s', (no,))
print(affected_rows)
# 第四步成功:手动提交
conn.commit()
except pymysql.MySQLError:
# 第四步失败:手动回滚
conn.rollback()
finally:
# 第五步:关闭连接释放资源
conn.close()
"""
Python连接 MySQL,更新数据
Author: RBHGO
"""
import pymysql
name = input('修改部门名称为:')
area = input('修改部门地址为:')
no = input('要修改部门编号为:')
conn = pymysql.connect(host='localhost', port=3306,
user='您设的用户名', password='您的密码',
database='hrs', charset='utf8mb4')
try:
# 第二步:获取游标对象
with conn.cursor() as cursor:
# 第三步:通过游标对象向数据库发出SQL语句获得执行结果
affected_rows = cursor.execute(
'update tb_dept set dname=%s, dloc=%s where dno=%s',
(name, area, no))
print(affected_rows)
# 第四步成功:手动提交
conn.commit()
except pymysql.MySQLError:
# 第四步失败:手动回滚
conn.rollback()
finally:
# 第五步:关闭连接释放资源
conn.close()
"""
Python连接 MySQL,查询
Author: RBHGO
"""
import pymysql
conn = pymysql.connect(host='localhost', port=3306,
user='您设的用户名', password='您的密码',
database='hrs', charset='utf8mb4')
try:
with conn.cursor() as cursor:
# 通过游标抓取数据
cursor.execute('select dno, dname, dloc from tb_dept')
# print(cursor.fetchone()) ---> 抓取一条
# print(cursor.fetchall()) ---> 抓取全部
# print(cursor.fetchmany()) ---> 抓取指定数量
dept_row = cursor.fetchone()
while dept_row:
print(f'部门编号: {dept_row[0]}')
print(f'部门名称:{dept_row[1]}')
print(f'部门所在地: {dept_row[2]}')
print('-' * 20)
dept_row = cursor.fetchone()
except pymysql.MySQLError as err:
print(err)
finally:
conn.close()
"""
将游标类型定为字典,通过键值对取值,操作性更加强
Author: RBHGO
"""
import pymysql
conn = pymysql.connect(host='localhost', port=3306,
user='您设的用户名', password='您的密码',
database='hrs', charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor)
try:
with conn.cursor(pymysql.cursors.DictCursor) as cursor:
# 通过游标抓取数据
cursor.execute('select dno as no, dname as name, dloc as loc from tb_dept')
dept_row = cursor.fetchone()
while dept_row:
print(dept_row.get('no'), dept_row.get('name'), dept_row.get('loc'))
dept_row = cursor.fetchone()
except pymysql.MySQLError as err:
print(err)
finally:
conn.close()
"""
面对对象
Author: RBHGO
"""
import pymysql
class Dept:
def __init__(self, no, name, location):
self.no = no
self.name = name
self.location = location
def __str__(self):
return f'编号:{self.no} \n名称:{self.name} \n所在地:{self.location}'
conn = pymysql.connect(host='localhost', port=3306,
user='您设的用户名', password='您的密码',
database='hrs', charset='utf8mb4')
try:
with conn.cursor() as cursor:
# 通过游标抓取数据
cursor.execute('select dno, dname, dloc from tb_dept')
dept_row = cursor.fetchone()
while dept_row:
print(dept_row.get('no'), dept_row.get('name'), dept_row.get('loc'))
dept_row = cursor.fetchone()
except pymysql.MySQLError as err:
print(err)
finally:
conn.close()
"""
批量插入
Author: RBHGO
"""
import time
import pymysql
conn = pymysql.connect(host='localhost', port=3306,
user='您设的用户名', password='您的密码',
database='hrs', charset='utf8mb4')
start = time.time()
try:
# 第二步:获取游标对象
with conn.cursor() as cursor:
params = []
for i in range(1, 50001):
params.append((f'user{i}',))
if i % 1000 == 0:
cursor.executemany(
'insert into tb_user(username) values (%s)',
params
)
params.clear()
conn.commit()
# 计时
end = time.time()
print(f'耗时{end - start:.2f}秒')
except pymysql.MySQLError:
# 第四步失败:手动回滚
conn.rollback()
finally:
# 第五步:关闭连接释放资源
conn.close()
削微再和大家分享一点其他知识,没有详细的分享不是他们不重要,是因为我没有经常使用,不是特别贯通,所以这里只能做一个介绍,如果有兴趣可以去查资料学习哦。
索引是关系型数据库中用来提升查询性能最为重要的手段。关系型数据库中的索引就像一本书的目录,我们可以想象一下,如果要从一本书中找出某个知识点,但是这本书没有目录这件是是极其糟糕的。创建索引虽然会带来存储空间上的开销,就像一本书的目录会占用一部分的篇幅一样,但是在牺牲空间后换来的查询时间的减少也是非常显著的,空间和时间总是没法两全的。
MySQL中,所有数据类型的列都可以被索引,常用的存储引擎InnoDB
和MyISAM
能支持每个表创建16个索引。InnoDB
和MyISAM
使用的索引其底层算法是B-tree
(B树),B-tree
是一种自平衡的树,类似于平衡二叉排序树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的操作都在对数时间内完成。
-- 简单来说:
-- 索引的本质就相当于是一个排好序的目录,加速查询
-- 索引通常要建在经常用户查询筛选条件的列上,这样才能有效的加速查询
-- 不要滥用索引,因为索引虽然加速了查询,但是会让插入数据的操作变得更慢
-- 对于InnoDB引擎来说,索引的底层是一个B+树,B+树是一个层次结构
-- 这种结构拥有极好磁盘I/O性能,一个4层的B+树就能应对10亿级的数据体量
-- 对于InnoDB引擎来说,主键上默认就会建索引,而且索引就是整张表的数据
-- 这种索引也称为聚集索引(只能一个),而我们自己创建的索引都是非聚集索引
在前面的分享中也提到,我们可以使用MySQL的explain
关键字来查看SQL的执行计划。
使用性能剖析系统:
explain select语句 —> 得到搜索语句的性能
通常情况下我们就看type和rows就可以
type性能(坏->好) ALL -> index -> range -> ref -> ref eg -> const/svstem
SQL执行计划中,会得到以下信息:
select_type
:查询的类型。
table
:查询对应的表。type
:MySQL在表中找到满足条件的行的方式,也称为访问类型,包括:ALL(全表扫描)、index(索引全扫描,只遍历索引树)、range(索引范围扫描)、ref(非唯一索引扫描)、eq_ref(唯一索引扫描)、const/system(常量级查询)、NULL(不需要访问表或索引)。在所有的访问类型中,很显然ALL是性能最差的,它代表的全表扫描是指要扫描表中的每一行才能找到匹配的行。possible_keys
:MySQL可以选择的索引,但是有可能不会使用。key
:MySQL真正使用的索引,如果为NULL就表示没有使用索引。key_len
:使用的索引的长度,在不影响查询的情况下肯定是长度越短越好。rows
:执行查询需要扫描的行数,这是一个预估值。extra
:关于查询额外的信息。
Using filesort
:MySQL无法利用索引完成排序操作。Using index
:只使用索引的信息而不需要进一步查表来获取更多的信息。Using temporary
:MySQL需要使用临时表来存储结果集,常用于分组和排序。Impossible where
:where
子句会导致没有符合条件的行。Distinct
:MySQL发现第一个匹配行后,停止为当前的行组合搜索更多的行。Using where
:查询的列未被索引覆盖,筛选条件并不是索引的前导列。-- 创建索引
create index idx_emp_ename on tb_emp (ename);
-- 前缀索引
create index idx_emp_ename on tb_emp (ename(1));
-- 索引的覆盖性问题
-- 如果查询的列没有被索引覆盖到,就会产生回表现象
-- 如果不希望产生回表现象,那么我们在创建索引时,可以使用复合索引尽可能覆盖查询需要的所有的列
select eno, ename, job from tb_emp where ename='曾霜';
-- 复合索引
drop index idx_emp_ename_job on tb_emp;
create index idx_emp_ename_job on tb_emp (ename, job);
explain select eno, ename, job from tb_emp where ename='曾';
explain select eno, ename, job from tb_emp where ename like '曾%' order by ename;
explain select eno, ename, job from tb_emp where ename like '曾%' order by sal;
explain select eno, ename, job from tb_emp
where ename='曾霜' and job='总裁';
-- 负向条件无法使用索引优化查询
explain select eno, ename, job from tb_emp where ename<>'曾霜';
explain select eno, ename, job from tb_emp where ename like '曾%';
-- 模糊查询如果左边使用通配符会导致索引失效
explain select eno, ename, job from tb_emp where ename like '%霜';
-- 在筛选条件中如果使用了函数也会导致索引失效
explain select eno, ename, job from tb_emp where concat(ename, '女士')='曾霜女士';
-- 下面的查询无法使用复合索引(or)
explain select eno, ename, job from tb_emp
where ename='曾霜' or job='总裁';
-- 下面的查询无法使用复合索引
explain select eno, ename, job from tb_emp where job='总裁';
-- 删除索引
drop index idx_emp_ename on tb_emp;
alter table tb_emp drop index idx_emp_ename;
-- 视图
-- 给查询生成一个快照,以后可以直接通过视图获得查询结果
-- 可以通过视图将用户访问数据的权限限制到具体的列上
-- 视图是一张虚拟的表.视图的可更新性要视具体情况而定(更新视图,其实是更新视图后面的表)
-- 创建视图
create view vi_emp_simple as
select eno, ename, job, dno from tb_emp;
-- 删除视图
drop view vw_dept_emp_count;
-- 如果希望更新视图,可以先用上面的命令删除视图,也可以通过`create or replace view`来更新视图。
视图的规则和限制。
order by
子句,但如果从视图中检索数据时也使用了order by
,那么该视图中原先的order by
会被覆盖。-- 存储过程(又称过程)是事先编译好存储在数据库中的一组SQL的集合,调用过程可以简化应用程序开发人员的工作,减少与数据库服务器之间的通信,对于提升数据操作的性能也是有帮助的。其实迄今为止,我们使用的SQL语句都是针对一个或多个表的单条语句,但在实际开发中经常会遇到某个操作需要多条SQL语句才能完成的情况。
-- 查看与安全相关的变量
show variables like '%safe%';
-- (存储)过程:将一系列的SQL组装到一起编译存储起来以备调用
-- 删除存储过程
-- drop procedure if exists sp_upgrade_emp_sal;
-- 创建存储过程
delimiter $$
create procedure sp_upgrade_emp_sal()
begin
set sql_safe_update=off;
update tb_emp set sal=sal+200 where dno=10;
update tb_emp set sal=sal+500 where dno=20;
update tb_emp set sal=sal+800 where dno=30;
end $$
delimiter ;
-- 调用存储过程直接修改数据库数据(hrs库中的部门编号对应员工工资增加)
call sp_upgrade_emp_sal();
在过程中,我们可以定义变量、条件,可以使用分支和循环语句,可以通过游标操作查询结果,还可以使用事件调度器。虽然我们说了很多过程的好处,但是在实际开发中,如果过度的使用过程并将大量复杂的运算放到过程中,必然会导致占用数据库服务器的CPU资源,造成数据库服务器承受巨大的压力。为此,我们一般会将复杂的运算和处理交给应用服务器,因为很容易部署多台应用服务器来分摊这些压力。
-- 删除函数
-- drop function truncate_string;
-- 用SQL语句封装函数,SQL是一种计算机语言,当然也是可以用来写函数的。
-- delimiter先把语句结束从;改为$$(写完记得改回来),在函数中还有 no sql声明这段不是sql语句(而是sql封装的函数)
delimiter $$
create function truncate_string(
content varchar(16000),
max_length int unsigned
) returns varchar(16000) no sql
begin
declare result varchar(16000) default '';
if length(content) > max_length then
set result=concat(substring(content, 1, max_length), '……');
else
set result=content;
end if;
return result;
end $$
delimiter ;
-- 测试
select truncate_string('漫漫长夜,思念幻想,准备好么?你的行囊!明天就要,独自去往,迎着正午,向着朝阳。', 25);
select
col_id,
col_name,
truncate_string(col_intro, 250)
from
tb_college;
感谢学习陪伴,您的点赞,评论就是我更新的动力