博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一视图

'''
视图是一个虚拟表,使用视图我们可以把查询过程中的临时表摘出来,用视图去实现,这样以后再想操作临时表的数据时就无需重写复杂的sql了,直接去视图中查找即可,但视图有明显的效率问题,并且视图是存放在数据库中的,如果程序中使用的sql过分依赖数据库中的视图,即强耦合,一旦sql需要修改且涉及到视图的部分,则必须去数据库中进行修改,就意味着扩展sql极为不便。
'''
1.创建视图
# 语法:create view 视图名称 as sql语句
create view teacher_view as select tid from teacher where tname='ldb';
# 使用
select cname from course where teacher_id=(select tid from teacher_view);
2.修改视图
# 语法:alter view 视图名称 as sql语句
alter view teacher_veiw as select * from course where cid>3;
3.删除视图
# 语法:drop view 视图名称
drop view teacher_view

二触发器

'''
使用触发器可以定制用户对表进行【增、删、改】操作时前后的行为,注意:没有查询
'''
1.创建触发器
# 插入前
create trigger tri_before_insert_tb1 before insert on tb1 for each row
begin
...
end
# 插入后
create trigger tri_after_insert_tb1 after insert on tb1 for each row
begin
...
end
# 删除前
create trigger tri_before_delete_tb1 before delete on tb1 for each row
begin
...
end
# 删除后
create trigger tri_after_delete_tb1 after delete on tb1 for each row
begin
...
end
# 更新前
create trigger tri_before_update_tb1 before update on tb1 for each row
begin
...
end
# 更新后
create trigger tri_after_update_tb1 after update on tb1 for each row
begin
...
end
2.使用触发器
'''
触发器无法由用户直接调用,而是由于对表的【增、删、改】操作被动引发的
'''
-- 准备表
create table cmd(
id int primary key auto_increment,
user char(32),
priv char(10),
cmd char(64),
sub_time datetime,  # 提交时间
success enum('yes','no')  # no代表执行失败
);
-- 错误日志表
create table errlog(
id int primary key auto_increment,
err_cmd char(64),
err_time datetime
);

-- 创建触发器
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中插入记录,触发触发器,根据if的条件决定是否插入错误日志
insert into cmd(
user,
priv,
cmd,
sub_time,
success
)
values
('ldb','0755','ls -l /etc',now(),'yes'),
('ldb','0755','cat /etc/passwd',now(),'no'),
('ldb','0755','useradd xxx',now(),'no'),
('ldb','0755','pa aux',now(),'yes');

-- 查询错误日志,发现有两条
select * from errlog;
'''
特别的:new表示即将插入的数据行,old表示即将删除的数据行
'''
3.删除触发器
drop trigger tri_after_insert_cmd;

三存储过程

'''
存储过程包含了一系列可执行的sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆sql
'''
1.创建简单存储过程(无参)
   -- 创建存储过程(无参)
   delimiter//
   create procedure p1()
   begin
       select * from blog;
       insert into blog(name,sub_time) values("xxx",now());
   end //
   delimiter;

   -- 在mysql中调用
   call p1()

   # 在python中基于pymysql调用
   cursor.callproc('p1')
   print(cursor.fetchall())
2.创建存储过程(有参)
# 对于存储过程,可以接收参数,有三类
in 仅用于传入参数用
out 仅用于返回值用
inout 既可以传入又可以当做返回值

   -- in:传入参数
   delimiter //
   create procedure p2(
       in n1 int,
       in n2 int
  )
   begin
       select * from blog where id>n1;
   end //
   delimiter;

   # 在mysql中调用
   call p2(2,1)

   # 在python中基于pymysql调用
   cursor.callproc('p2',(2,1))
   print(cursor.fetchall())

   -- out:返回值
   delimiter //
   create procedure p3(
       in n1 int,
       out res int
  )
   begin
       select * from blog where id>n1;
       set res=1;
   end //
   delimiter;

   # 在mysql中调用
   set @res=0;  # 0代表执行失败,1代表执行成功
   call p3(3,@res);
   select @res;

   # 在python中基于pymysql调用
   cursor.callproc('p3',(3,0))
   print(cursor.fetchall())

   cursor.execute('select @_p3_0,@_p3_1;')  # @_p3_0代表第一个参数,@_p3_1代表第二个参数,即返回值
   print(cursor.fetchall())

   -- inout:既可以传入又可以返回
   delimiter //
   create procedure p4(
       inout n1 int
  )
   begin
       select * from blog where id>n1;
       set n1=1;
   end //
   delimiter;

   # 在mysql中调用
   set @x=3;
   call p4(@x);
   select @x;

   # 在python中基于pymysql调用
   cursor.callproc('p4',(3,))
   print(cursor.fetchall())

   cursor.execute('select @_p4_0;')
   print(cursor.fetchall())
# 事务
#介绍
delimiter //
           create procedure p4(
               out status int
          )
           BEGIN
               1. 声明如果出现异常则执行{
                   set status = 1;
                   rollback;
              }
                 
               开始事务
                   -- 由秦兵账户减去100
                   -- 方少伟账户加90
                   -- 张根账户加10
                   commit;
               结束
               
               set status = 2;
               
               
           END //
           delimiter ;

#实现
delimiter //
create PROCEDURE p5(
   OUT p_return_code tinyint
)
BEGIN
   DECLARE exit handler for sqlexception  # declare handler声明异常
   BEGIN
       -- ERROR
       set p_return_code = 1;
       rollback;
   END;

   DECLARE exit handler for sqlwarning
   BEGIN
       -- WARNING
       set p_return_code = 2;
       rollback;
   END;

   START TRANSACTION;
       DELETE from tb1; #执行失败
       insert into blog(name,sub_time) values('yyy',now());
   COMMIT;

   -- SUCCESS
   set p_return_code = 0; #0代表执行成功

END //
delimiter ;

#在mysql中调用存储过程
set @res=123;
call p5(@res);
select @res;

#在python中基于pymysql调用存储过程
cursor.callproc('p5',(123,))
print(cursor.fetchall()) #查询select的查询结果

cursor.execute('select @_p5_0;')
print(cursor.fetchall())
# 在mysql中执行存储过程
-- 无参数
call proc_name()

-- 有参数,全in
call proc_name(1,2)

-- 有参数,有in,out,inout
set @t1=0;
set @t2=3;
call proc_name(1,2,@t1,@t2)
# 在python中基于pymysql执行存储过程
import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 执行存储过程
cursor.callproc('p1', args=(1, 22, 3, 4))
# 获取执行完存储的参数
cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3")
result = cursor.fetchall()

conn.commit()
cursor.close()
conn.close()


print(result)

3.删除存储过程
drop procedure proc_name;

四函数

mysql中提供了许多内置函数

#1 基本使用
mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y');
       -> 'Sunday October 2009'
mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s');
       -> '22:23:00'
mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00',
   ->                 '%D %y %a %d %m %b %j');
       -> '4th 00 Thu 04 10 Oct 277'
mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00',
   ->                 '%H %k %I %r %T %S %w');
       -> '22 22 10 10:23:00 PM 22:23:00 00 6'
mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V');
       -> '1998 52'
mysql> SELECT DATE_FORMAT('2006-06-00', '%d');
       -> '00'


#2 准备表和记录
CREATE TABLE blog (
   id INT PRIMARY KEY auto_increment,
   NAME CHAR (32),
   sub_time datetime
);

INSERT INTO blog (NAME, sub_time)
VALUES
  ('第1篇','2015-03-01 11:31:21'),
  ('第2篇','2015-03-11 16:31:21'),
  ('第3篇','2016-07-01 10:21:31'),
  ('第4篇','2016-07-22 09:23:21'),
  ('第5篇','2016-07-23 10:11:11'),
  ('第6篇','2016-07-25 11:21:31'),
  ('第7篇','2017-03-01 15:33:21'),
  ('第8篇','2017-03-01 17:32:21'),
  ('第9篇','2017-03-01 18:31:21');

#3. 提取sub_time字段的值,按照格式后的结果即"年月"来分组
SELECT DATE_FORMAT(sub_time,'%Y-%m'),COUNT(1) FROM blog GROUP BY DATE_FORMAT(sub_time,'%Y-%m');

#结果
+-------------------------------+----------+
| DATE_FORMAT(sub_time,'%Y-%m') | COUNT(1) |
+-------------------------------+----------+
| 2015-03                       |        2 |
| 2016-07                       |        4 |
| 2017-03                       |        3 |
+-------------------------------+----------+
3 rows in set (0.00 sec)
# 自定义函数
'''
注意!!!
函数中不能写sql语句,函数仅仅只是一个功能,是一个在sql中被应用的功能
'''
delimiter //
create function f1(
   i1 int,
   i2 int)
returns int
BEGIN
   declare num int;
   set num = i1 + i2;
   return(num);
END //
delimiter ;

delimiter //
create function f5(
   i int
)
returns int
begin
   declare res int default 0;
   if i = 10 then
       set res=100;
   elseif i = 20 then
       set res=200;
   elseif i = 30 then
       set res=300;
   else
       set res=400;
   end if;
   return res;
end //
delimiter ;
# 执行函数
# 获取返回值
select UPPER('egon') into @res;
SELECT @res;
# 在查询中使用
select f1(11,nid) ,name from tb2;
# 删除函数
drop function func_name;

五流程控制

# 条件语句
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 ;
# 循环语句
delimiter //
CREATE PROCEDURE proc_while ()
BEGIN

   DECLARE num INT ;
   SET num = 0 ;
   WHILE num < 10 DO
       SELECT
           num ;
       SET num = num + 1 ;
   END WHILE ;

END //
delimiter ;
# repeat循环
delimiter //
CREATE PROCEDURE proc_repeat ()
BEGIN

   DECLARE i INT ;
   SET i = 0 ;
   repeat
       select i;
       set i = i + 1;
       until i >= 5
   end repeat;

END //
delimiter ;
# loop循环
BEGIN
   
   declare i int default 0;
   loop_label: loop
       
       set i=i+1;
       if i<8 then
           iterate loop_label;
       end if;
       if i>=10 then
           leave loop_label;
       end if;
       select i;
   end loop loop_label;

END

六事务

# 事务是什么
事务是应用程序中一系列严密的操作,所有的操作必须成功完成,否则在每个操作中所作的所有更改都会被撤销
# 事务的四大特性
1.原子性:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。不能够单独运行
2.一致性:在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏
3.隔离性:强调的是多个用户并发访问数据库的时候,一个用户事务不能被其他用户的事务所干扰到,多个并发事务之间的数据要相互隔离
4.持久性:一个事务一旦被提交,该事务对数据库所做的更改便持久的保存在数据库之中
# 什么是脏数据,脏读,不可重复读,幻读
脏读:当一个事务正在访问数据,并对数据进行了修改,但是没有提交,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为他访问的数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所作的操作可能是不正确的
   不可重复读:在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读
   幻读:指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好像发生了幻觉一样
       
# mysql中事务启动的两种方式
begin或者start transcation

七索引

# 什么是索引
索引是存储引擎中一种数据结构,或者说数据的组织方式,又称之为键key
为数据建立索引就好比是为书创建目录
# 索引的优缺点
优点:
   可以给任意字段设置索引
   加快数据的查询速度
缺点:
   创建索引和维护索引要耗费时间
   索引也需要占空间,如果我们有大量的索引,索引文件可能会比数据文件更快达到上限值
   当对表中数据进行增加、修改、删除时,索引也需要动态的维护,降低了数据的维护速度
# 索引的使用原则
对经常更新的表就避免对其进行过多的索引,对经常用于查询的字段应该创建索引
对数据量小的表最好不要使用索引

索引到底是一种什么样的数据结构:B+
   二叉树、平衡二叉树、B树=》B+
   
# 二叉查找树的特点
任何节点的左子节点的键值都小于当前节点键值,右子节点的键值都大于当前节点的键值。顶端的节点我们称为根节点,没有子节点的节点我们称之为叶节点(左节点小于当前节点,右节点大于当前节点)
   
# 平衡二叉树
又称AVL树,在满足二叉查找树特性的基础上,要求每个节点的左右子树的高度不能超过1
   
B+树优点:
   1、在二叉树、平衡二叉树、B树的基础上做了进一步优化,
       只有叶子节点放真正的数据,这意味着在等量数据的前提下,B+树的高度是最低的
   2、B+的叶子节点都是排好序的,这意味着在范围查询上,B+树比B树更快,
       快就快在一旦找到一个树叶节点,就不需要在再从树根查起了
       
innodb存储引擎索引分类:
   1、hash索引:更适合等值查询,不适合范围查询
   2、B+树索引
       聚集索引/聚簇索引-》以主键字段的值作为key创建的索引(一张表中只有一个)
       辅助索引:针对非主键字段创建的索引(一张表中可以有多个)
       
举例:
id字段为主键
我们为name字段创建了辅助索引

select name,age,gender from user where id =3;  # 用的是聚集索引,所以压根不需要回表
select name,age,gender from user where name="egon";  # 需要回表

回表查询:通过辅助索引拿到主键值,然后再回到聚集索引从根再查一下
覆盖索引:不需要回表就能拿到你要的全部数据
   select name,age,gender from user where name="egon";  # 没有覆盖索引,需要回表

   select name,id from user where name="egon";  # 覆盖了索引
   
# 索引的简单使用
create table t1(
   id int,
   name varchar(10),
);

create index idx_xxx on t1(id);
drop index idx_xxx on t1;

# 索引下推技术(默认开启)

# 最左前缀匹配原则
create index idx_id_name_gender on s1(id,name,gender);
当查询条件中出现以下字段,可以命中联合索引,因为符合最左前缀原则
id
id name
id gender
id name gender
查询条件中只出现一个gender字段,不能命中

八sql注入

import pymysql

conn=pymysql.connect(host="127.0.0.1",port=3306,database="db01",user="root",password="123")
cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)

name=input('username>>>:').strip()  # xxx" or 1=1 -- sdrfsd
pwd=input('password>>>:').strip()

# sql='select * from user where username="%s" and password="%s"' %(name,pwd)
# print(sql)
# rows=cursor.execute(sql)

rows=cursor.execute('select * from user where username=%s and password=%s',args=(name,pwd))  # 里面引号不用加
if rows:
   print('login successful')
else:
   print('name or pwd error')

cursor.close()
conn.close()

import pymysql
conn=pymysql.connect(host="127.0.0.1",port=3306,database="db01",user="root",password="123")
cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)

cursor.execute("insert into user values(4,'xxx','123');")
conn.commit()
cursor.close()
conn.close()