python四十四课---SQL注入问题,视图,触发器,事务的特性,事务的隔离级别,存储过程(自定义函数),流程控制,内置函数,索引与慢查询优化,建立索引的原则
昨日内容回顾
-
多表查询的两种方式
连表操作 inner join left join right join ps:所需结论来自于多张表的字段 建议使用连表 子查询 将SQL语句括号括起来当做另外一条SQL语句的条件 ps:所需结论来自于一张表的字段 可以使用子查询 '''甚至两者在复杂SQL查询中需要混合使用'''
-
小知识点的补充说明
1.拼接相关操作 concat() concat_ws() group_concat() 2.关键字exists sql1 exists sql2 """ if sql2:sql1 """ 3.针对表操作的SQL补充 alter table 表名 rename\add\change\modify\drop
-
可视化软件之Navicat
1.下载与安装 2.基本使用 链接、创建、库、表、记录、外键 逆向数据库到模型 新建查询 运行、转储SQL文件
-
多表查询练习题
1.先确定需要涉及到几张表 2.大致预览表结构与表数据 3.根据已知条件确定切入点 4.灵活运用所学内容去拼凑 将复杂的操作流程化 步骤化 ps:一定要自己多练多敲
-
pymysql模块
pip3 install pymysql import pymysql conn = pymysql.connect( host,port,user,password/passwd,database/db,charset,autocommit ) cursor = conn.cursor(...) cursor.execute(sql) cursor.fetchall() 1.获取执行结果的方法也有类似于文件光标的特性 2.针对增、删、改操作需要二次确认 conn.commit() autocommit = True
今日内容概要
- SQL注入问题
- 视图
- 触发器
- 存储过程
- 流程控制
- 内置函数
- 索引与慢查询优化
今日内容详细
SQL注入问题
怪像1:输对用户名就可以登录成功
怪像2:不需要对的用户名和密码也可以登录成功
import pymysql
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123',
database='db5',
charset='utf8',
autocommit=True
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
username = input('username>>>:').strip() # 1.获取用户名和密码
password = input('password>>>:').strip()
# 2.构造查询SQL语句
sql = " select * from userinfo where name='%s' and pwd='%s' " % (username, password)
# 模拟登录的时候讲用户输入的用户名与密码拼成一条sql语句,查询表里面的数据,where后面是查询条件!!!
cursor.execute(sql) # 执行sql语句 会出sql注入问题
# 发送给服务端执行SQL语句
# sql = " select * from userinfo where name=%s and pwd=%s "
# cursor.execute(sql, (username, password)) 这样不会出sql注入问题
# 4.获取执行结果
res = cursor.fetchall()
if res:
print('登录成功')
print(res)
else:
print('用户名或密码错误')
----------------------------------------------------
SQL注入:利用特殊符合的组合产生特殊的含义 从而避开正常的业务逻辑
select * from userinfo where name='jason' -- kasdjksajd' and pwd=''
select * from userinfo where name='xyz' or 1=1 -- aksdjasldj' and pwd=''
-------------------------------------
针对上述的SQL注入问题
核心在于:利用格式化手动拼接了关键数据!!!
针对sql语句的拼接,不要自己拼接!!
应该是: 交给execute处理即可!!!
sql = " select * from userinfo where name=%s and pwd=%s " # 只需要%s 占位
cursor.execute(sql, (username, password))
# execute方法可以自动识别sql语句里面的占位符,
# 并且还可以自动按位置往占位符上面传你放在元组里面的数据!!!
------------------------------------------------------
'''
补充说明:executemany(sql,[(),(),(),()...])
'''
sql = " insert into userinfo(name,pwd) values(%s,%s)" # 模拟注册的语法了
cursor.executemany(sql, [('oscar', '123'), ('maria', '333'), ('lili', '567')])
# 一次性发送多条sql语句给服务端
# sql后面的列表里面有几个元组数据,就可以生成几条sql语句!!! 列表套元组的形式[(,),(,),(,),(,)]
-------------------------------------------------------
怪像1:输对用户名就可以登录成功!!!
.
.怪像2:不需要对的用户名和密码也可以登录成功!!!
.
由于输入的用户名 jason' --kasdjksa 里面带了一个小引号'
导致输入的用户名代替 格式化输出的占位符 %s 时 会让jason' 与 %s 两边的'' 起作用起来
转到sql里面就看的更清楚了,原来where后面是有两个条件都要成立的,由于jason后面的一个引号
以及后面--符号 --符号后面的剩余用户名部分与剩余的sql语句部分,直接变成了sql语句的注释了!!! 利用单引号'和sql里面的注释语法-- 把and pwd='%s' 这一段sql语句直接注掉了!!!
换句话说就算密码瞎写,也能登录成功!!!只要把用户名输对再加个单引号' -- 就能登录成功了
.
.同理当随便输一个用户名xyz' or 1=1 --xxxxxxxx
利用单引号和'%s' 左边的引号起作用,这个时候 or 1=1 就变成语法结构了 后面由于有--全部都变成注释了 这个时候语法就变成了 where name = 'xyz' or 1=1 此时用户名就算不对,因为连接是or 1=1 所有条件肯定成立,所以select能拿到所有数据!!!肯定能拿到所有数据!!
.
.
.
.
.
.
视图
视图就是:将一条sql语句产生的结果看成是一张虚拟表,然后保存下来,后续可以直接使用!!!
---------------------------------------------------------
create view teacher2course as select * from teacher inner join course on teacher.tid = course.teacher_id;
create view 给该视图表起的别名 as 某个sql语句生成的虚拟表;
这样就利用该sql语句生成的虚拟表生成了一个视图表了
----------------------------------------
结论:
1.视图的表只能用来查询不能做其他增删改操作,因为视图表里面的数据来源于源表!!!
2.视图尽量少用!!!
会跟真正的表产生混淆 !!! 从而干扰操作者!!!
一条sql语句的结果,可以看成是一张表!!!
.
.
.
.
.
.
.
.
.
触发器
达到某个条件之后自动触发执行。
在MySQL中更加详细的说明是触发器:针对表继续增、删、改操作能够自动触发!!!!!!
主要有六种情况:增前、增后、删前、删后、改前、改后
触发器要自己来定义!!!一旦触发触发器的执行,就会自动运行begin与end中间的sql语句代码!!!
------------------------------------------------
------------------------------------------------
如何创建触发器:
create trigger 触发器的名字 before/after insert/update/delete on 表名 for each row
begin
触发了触发器后执行的sql语句!!!
end
------------------------------------------------
before/after 选一个
insert/update/delete 选一个
------------------------------
比如:
create trigger 触发器的名字1 before insert on 表名1 for each row
在对表名1做插入操作前,触发触发器的执行
------
create trigger 触发器的名字2 after delete on 表名2 for each row
在对表名2做删除操作后,触发触发器的执行
---------------------------------------------------
1.触发器命名有一定的规律
tri_before_insert_t1 # 对t1表插入操作前,触发的触发器命名
tri_after_delete_t2 # 对t2表删除操作后,触发的触发器命名
tri_after_update_t2 # 对t2表更新操作后,触发的触发器命名
--------------------------------------------------
.
.
.
临时修改SQL语句的结束符
因为有些操作中需要使用分号
再mysql里面如果需要替换sql语句的结束符,就需要用到关键字delimiter
关键字:delimiter
delimiter $$ # 将mysql默认的结束符由分号; 换成$$
.
.
触发器实际应用
# 触发器的作用:可以对一张表做监控,一旦一张表里面出现了一些我想要的数据,
# 就可以用触发器自动将这些信息插入到另外一张表里面去!!!
# 将来通过看另外一张表表就能直接看到想到的一些数据了!!!
下面的触发器的作用是,当往cmd表里面插入的数据的字段success对应的值是no,触发触发器,
由触发器往errlog表里面自动插入一条数据,该数据记录了插入到cmd表里面的cmd字段对应的数据和
sub_time字段对应的数据!!!
创建两个表:
CREATE TABLE cmd (
id INT PRIMARY KEY auto_increment,
USER CHAR (32),
priv CHAR (10),
cmd CHAR (64), # cmd 命令
sub_time datetime, # 提交时间
success enum ('yes', 'no') # 0代表执行失败
);
-----------------
CREATE TABLE errlog (
id INT PRIMARY KEY auto_increment,
err_cmd CHAR (64),
err_time datetime
);
-------------------------------------------------
触发器代码:在编写触发器前将结束符有分号变成$$
因为触发器代码里面需要使用到分号!!!
delimiter $$ # 将mysql默认的结束符由分号; 换成$$
create trigger tri_after_insert_cmd after insert on cmd for each row
# 给cmd表 插入数据后,自动触发,
begin
if NEW.success = 'no' then
# 每新插入一条数据,都会被MySQL封装成NEW对象,然后拿到该数据success字段对应的值判断
# 如果对应的值为no 如果是no 就往errlog表里面插入一条数据!该数据记录了cmd命令与报错时间
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
('kevin','0755','ls -l /etc',NOW(),'yes'),
('kevin','0755','cat /etc/passwd',NOW(),'no'),
('kevin','0755','useradd xxx',NOW(),'no'),
('kevin','0755','ps aux',NOW(),'yes');
NOW()相当于python里面的time模块,用来获取当前的时间!!!
---------------------
select * from errlog; # 查询errlog表记录
show triggers\G; # 查看所有的触发器
drop trigger tri_after_insert_cmd; # 也可以删除触发器!!
.由触发器触发自动往errlog表里面添加了!!!
.
.
.
.
.
.
.
事务 重要
事务(transaction)是作为一个单元的一组有序的数据库操作。
如果组中的所有操作都成功,则认为事务成功,即使只有一个操作失败,事务也不成功。
如果所有操作完成,事务则提交,其修改将作用于所有其他数据库进程。
如果一个操作失败,则事务将回退到所有操作都没执行的状态,该事务所有操作的影响都将取消。
事务处理的几个术语:回退(rollback) 提交(commit)
-------------------------------------------------------------
事务的四大特性(ACID)
A:原子性 Atomicity
事务中的各项操作是不可分割的整体,要么同时成功要么同时失败!!!
C:一致性 Consistency
使数据库从一个一致性状态变到另一个一致性状态!!!
I:隔离性 Isolation
多个事务之间彼此不干扰!!!
D:持久性 Durability
也称永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的!!!
-----------------------------------------------------------
create table user(
id int primary key auto_increment,
name char(32),
balance int
);
insert into user(name,balance)
values
('jason',1000),
('kevin',1000),
('tank',1000);
# 修改数据之前先开启事务操作,如果想要创建事务,必须要先用关键字申明,否则不行!!
start transaction;
# 修改操作
update user set balance=900 where name='jason'; #买支付100元
update user set balance=1010 where name='kevin'; #中介拿走10元
update user set balance=1090 where name='tank'; #卖家拿到90元
# 事务的回退功能,回滚到上一个状态
rollback;
# 开启事务之后,只要没有执行commit操作,修改的数据其实是在内存里面,硬盘里面的数据还没有真正改了,数据其实都没有真正刷新到硬盘
commit;
"""
事务相关关键字
start transaction; # 开启事务
rollback; # 回滚
commit; # 事务一旦确认后就不再支持回滚操作了!!!
savepoint; # 节点 设置多个回滚点 控制事务的回滚到指定节点的位置
"""
.
事务确认后,再回滚就没有用了!!!
.
.
.
.
.
.
.
事务的隔离级别
在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改
InnoDB支持所有隔离级别
set transaction isolation level 级别
--------------------------------------------------
1.read uncommitted(未提交读)
事务中的修改即使没有提交,对其他事务也都是可见的,事务可以读取未提交的数据,这一现象也称之为"脏读"
换句话说:当一个事务没有提交确认的时候,另外一个事务在读取数据的时候就会出现'脏读',
因为它读的根本不是硬盘里面真正的数据,而是读的内存里面的未提交确认的数据!!!
--------------------------------------------------
2.read committed(提交读)
大多数数据库系统默认的隔离级别!!!
一个事务从开始直到提交之前所作的任何修改,对其他事务都是不可见的,这种级别也叫做"不可重复读"
--------------------------------------------------
3.repeatable read(可重复读) # MySQL默认隔离级别
能够解决"脏读"问题,但是无法解决"幻读"
幻读:
指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,
当之前的事务再次读取该范围的记录会产生幻行。
InnoDB和XtraDB通过多版本并发控制(MVCC)及间隙锁策略解决该问题
--------------------------------------------------
4.serializable(可串行读)
强制事务串行执行,很少使用该级别(就是让多个事务强制串行,不并发执行了,就会导致执行效率很低)
--------------------------------------------------
.
.
.
.
.
MVCC机制
MVCC,Multi-Version Concurrency Control ,多版本并发控制。
MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,以提高并发性能。
------------------------
怎么解决多版本并发操作下,数据不会错乱的问题?
给数据加一些唯一标识,数据一被改后,唯一标识就会改变,
这样当一个事务开始读取一个数据的时候会记录一下该数据的标识,
这样当一个事务在准备动数据时,会再次检查一下该数据的标识,
如果发现数据标识改变了,说明数据已经被其他的事务给改过了,
需要重新再拿一下该数据,
直到当拿该数据走的时候的标识和后面想要改该数据时,再次查看该数据的标识与原来一样
说明这期间没有其他事务改该数据,说明此时的数据是真正的数据!!!
才能对该数据进行操作!!!
就是利用唯一标识来判断,数据在改之前,有没有被其他的人改过!!!
-------------------------------------------
MVCC只能在read committed(提交读)、repeatable read(可重复读)两种隔离级别下工作,
其他两个不兼容(read uncommitted:总是读取最新 serializable:所有的行都加锁)
-----------------------------------------------------
.
.
.
.
.
.
怎么解决多版本并发操作下,数据不会错乱的问题
InnoDB的MVCC通过在每行记录后面保存两个隐藏的列来实现MVCC
一个列保存了行的创建时间
一个列保存了行的过期时间(或删除时间) # 本质是系统版本号
-------------------------------------------------------
每开始一个新的事务,版本号都会自动递增,
事务开始时刻的系统版本号,会作为事务的版本号,用来与查询到的每行记录版本号进行比较
例如
刚插入第一条数据的时候,我们默认事务id为1,实际是这样存储的
username create_version delete_version
jason 1
可以看到,我们在content列插入了jason这条数据,
在create_version这列存储了1, 1是这次插入操作的事务id。
---------------------------------------
然后我们将jason修改为jason01,实际存储是这样的
username create_version delete_version
jason 1 2
jason01 2
可以看到,update的时候,会先将之前的数据delete_version标记为当前新的事务id,也就是2,
然后将新数据写入,将新数据的create_version标记为新的事务id
---------------------------------------
当我们删除数据的时候,实际存储是这样的
username create_version delete_version
jason01 2 3
"""
由此当我们查询一条记录的时候,只有满足以下两个条件的记录才会被显示出来:
1.当前事务id要大于或者等于当前行的create_version值,这表示在事务开始前这行数据已经存在了。
2.当前事务id要小于delete_version值,这表示在事务开始之后这行记录才被删除。
"""
.
.
.
.
.
.
.
.
.
.
.
存储过程 可以看成是python中的自定义函数
存储过程包含了一系列的可执行的sql语句,存储过程存放于mysql中,通过调用它的名字,
可以执行其内部的一堆sql语句。
相当于在mysql里面写自定义函数,然后通过函数名加括号执行函数体里面的sql代码!!!
------------------------------------------------
create procedure p1() 类似于python里面的 def p1(): 定义一个函数
# 无参函数
delimiter $$
create procedure p1()
begin
select * from cmd;
end $$
delimiter;
# 调用
call p1();
-------------------------------------------------
-------------------------------------------------
# 有参函数
delimiter $$
create procedure p2(
in m int, # in表示这个参数必须只能是传入不能被返回出去
in n int,
out res int # out表示这个参数可以被返回出去,还有一个inout表示即可以传入也可以被返回出去
)
begin
select * from cmd where id > m and id < n;
set res=0; # 用来标志存储过程是否执行
end $$
delimiter ;
-------------------------------------------------
"""
查看存储过程具体信息
show create procedure pro1;
查看所有存储过程
show procedure status;
删除存储过程
drop procedure pro1;
"""
-------------------------------------------------
# 针对res需要先提前定义
set @res=10; # 定义变量名对应的值
select @res; # 查看该变量名对应的值
call p1(1,5,@res) # 调用p1有产函数
select @res # 再次查看该变量名对应的值,如果执行成功变量名@res的值就已经改变了
# 大前提:存储过程在哪个库下面创建的只能在对应的库下面才能使用!!!
# 1、直接在mysql中调用
set @res=10 # res的值是用来判断存储过程是否被执行成功的依据,所以需要先定义一个变量@res存储10
call p1(2,4,10); # 直接传数字会报错,需要用变量接收一下数字再传进函数
call p1(2,4,@res);
# 查看结果
select @res; # 执行成功,@res变量值发生了变化
# 2、在python程序中调用
pymysql链接mysql
产生的游表cursor.callproc('p1',(2,4,10)) # 内部原理:@_p1_0=2,@_p1_1=4,@_p1_2=10;
cursor.excute('select @_p1_2;')
.
.
.
.
.
.
.
.
.
.
mysql中的内置函数 类似于python中的内置函数
补充:可以通过在mysql里面输入命令 help 函数名
在mysql里面查看帮助信息!!!
--------------------------------------
# 1.移除指定字符
Trim、LTrim、RTrim
--------------------------------------
# 2.大小写转换
Lower、Upper
--------------------------------------
# 3.获取左右起始指定个数字符(切片操作)
Left、Right
--------------------------------------
# 4.返回读音相似值(对英文效果)
Soundex
--------------------------------------
"""
eg:客户表中有一个顾客登记的用户名为J.Lee
但如果这是输入错误真名其实叫J.Lie,可以使用soundex匹配发音类似的
where Soundex(name)=Soundex('J.Lie')
"""
---------------------------------------
# 5.日期格式化 date_format 重要!!!
'''在MySQL中表示时间格式尽量采用2022-11-11形式'''
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');
------------
# 对sub_time字典中的年月进行分组
select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');
1.where Date(sub_time) = '2015-03-01'
2.where Year(sub_time)=2016 AND Month(sub_time)=07;
# 更多日期处理相关函数
adddate 增加一个日期
addtime 增加一个时间
datediff计算两个日期差值
.
.
切片操作
.
.
.
.
.
.
.
.
.
流程控制
# 分支结构
declare i int default 0;
IF i = 1 THEN
SELECT 1;
ELSEIF i = 2 THEN
SELECT 2;
ELSE
SELECT 7;
END IF;
# 循环结构
DECLARE num INT ;
SET num = 0 ;
WHILE num < 10 DO
SELECT num ;
SET num = num + 1 ;
END WHILE ;
.
.
.
.
.
.
.
.
.
.
索引相关概念 重要
1)索引就好比一本书的目录,它能让你更快的找到自己想要的内容
2)就是用来加快数据查询的 !!! 基于索引到数据库里查数据速度会更快!!!
索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构
* primary key
* unique key
* index key
1.上述的三个key都可以加快数据查询
2.primary key和unique key除了可以加快查询本身还自带限制条件
而index key很单一就是用来加快数据查询
3.外键不属于索引键的范围 是用来建立关系的 与加快查询无关
------------------------------------------------
索引加快查询的本质:
# 如下userinfo表里面有如下字段,并且表里面有了很多数据
id int primary key auto_increment,
name varchar(32) unique,
province varchar(32)
age int
phone bigint
# 现在需要查询数据,基于id与name字段去查询数据会比较快
select name from userinfo where phone=18818888888;
# 不使用索引字段去查数据,就比较慢,需要一条一条的去对比,才能找到
select name from userinfo where id=99999;
# 用索引字段去查数据,直接按id定位了 ,查询速度就比较快了
# 索引的优缺点:
索引可以加快数据查询,但是会降低增删的速度,
因为每对表里面的数据增删一次,
都会把之前的所做的索引全部删掉,索引重新生成。所以一旦数据库里面的数据很多的情况下
对数据进行增删的时候,都会重新生成索引,就会导致增删数据的速度特别慢
# 通常情况下我们频繁使用某些字段查询数据的时候
# 为了提升查询的速度,可以将该字段建立索引!!! index key
--------------------------------------------------
聚集索引(primary key)
主键、主键索引
辅助索引(unique,index)
除主键意外的都是辅助索引
覆盖索引
select name from user where name='jason'; # 实际不会出现,只是举个例子
非覆盖索引 # 比较常见 要查询的字段,与索引字段不是同一个的情况
select age from user where name='jason';
----------------------------------------------------
.
.
.
.
.
.
.
.
索引数据结构
索引底层是树结构,树是计算机底层的数据结构
树的类型有哪些 ?
二叉树 b树 b+树 b*树
根节点:一个 B-树索引只有一个根节点,实际上就是位于树的最顶端的分支节点。
分支节点:包含的条目指向索引里其他的分支节点或者叶子节点。
叶子节点:包含的条目直接指向表里的数据行。叶子节点之间彼此相连,
一个叶子节点有一个指向下一个叶子节点的指针。
# 辅助索引的特点是,树结构的叶子节点存放的是数据的主键值!!!
# 不能根据辅助索引单独的去查询数据的!!!
# 所以根据辅助索引查数据,先要走辅助索引树拿到数据的主键值
# 再走数据数,根据主键值,找到叶子节点上的数据!!!
--------------------------------------------------------------
# 树结构每一个节点能够存储的数据总大小是固定的!!!
# 所以为什么要推荐用id字段作为表的组件,因为数字类型数据占的内存最小
# 所以一个节点能存的索引,是最多的,所以这样一个节点的指针就可以是最多的
# 树的层级越矮,找到数据的步骤就越少,所以要尽可能的降低树的层级
# 这样很多个表如果数据量一样,以id为索引,树的结构层级就会是最矮的
# 这样就利于减少数据查找的时间
# 单一节点里面能够存储的数据越多,树的层级就越矮,查询数据的速度就越快!!!
-------------------------------------------------------------
# 二叉树:
二叉树的特点:每个节点最多有2个分叉,左子树和右子树数据顺序左小右大。
# B树:
B树中所有的节点,都可以存放完整真实的数据,每个节点的分叉没有限制
# B+树:
只有叶子节点才会存放真正的数据,其他节点只会存放索引数据!!!
# B*树:
只有叶子节点才会存放真正的数据,其他节点只会存放索引数据!!!
-------------------------------------------
二叉树
.
.
b树,每个节点的分叉没有限制,叶子节点没有指向旁边叶子节点间的指针
.
.
.
B+树索引 分支节点与根节点上存储的是数据的主键值,
比如找索引是26的数据,根节点上已经标识了索引的区间了
只需要第一次判断出26在左边区间5里面,第二次判断出在20区间里面,第三次就能找到26了
p就是指针的意思,会指向另一个节点,让磁头去读指向的另一个地方。
也就意味着,有了树型结构,按照索引找数据,只需要3步
树的层级越矮,找到数据的步骤就越少,所以要尽可能的降低树的层级
B+树索引 叶子节点有直接指向其他叶子节点的指针 这样想要查询多个索引的数据的时候
B+树索引结构,就不需要每一个索引都从根节点开始找了!!!
.
.
.
B*树索引 更牛逼,在支节点也添加了指向相邻节点的指针!!!
这样批量查询数据的速度就更快乐
.
.
.
.
怎么样在sql里面添加索引字段
#创建索引
alter table test add index index_name(name);
#创建索引
create index index_name on test(name);
#查看索引
desc table;
#查看索引
show index from table;
#删除索引
alter table test drop key index_name;
#添加主键索引(略)
#添加唯一性索引
alter table student add unique key uni_xxx(xxx);
#查看表中数据行数
select count(*) from city;
#查看去重数据行数
select count(distinct name) from city;
-------------------------------------------
# 有时候就算采取索引字段查询数据,也可能不会走索引!!!
1.没有查询条件,或者查询条件没有建立索引 select * from table;
2.查询结果集是原表中的大部分数据,应该是25%以上
3.索引本身失效,统计数据不真实
4.查询条件使用函数在索引列上
或者对索引列进行运算,就是条件里面有数学表达式 加减乘除 的这种
select * from test where id-1=9
5. < > ,not in 等这些有可能不走索引 select * from tab where phone > 155;
6. 模糊查询的时候 %在最前面,也是不走索引的,因为%在最前面意味着匹配任意个数任意字符
-------------------------------------------
.
.
.
.
.
.
.
.
慢查询优化
有时候sql语句执行起来非常的慢,怎么知道这条sql的执行效率如何
关键字:explain
在sql语句的前面加上explain,能够自动判断该条sql写的怎么样
explain select name,countrycode from city where id=1;
会返回sql查询的等级,标识出写的sql到底如何,看结果里面type字段对应的值!!
7种类型的等级
1)index # index类型只遍历索引树
2)range # 范围查询一般是该等级
3)ref
4)eq_ref
5)const
6)system
7)null
# 从上到下,性能从最差到最好,我们认为至少要达到range级别就行了
.
.
.
.
.
.
.
建立索引的原则
1、选择唯一性索引 唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
主键索引和唯一键索引,在查询中使用是效率最高的。
注意:如果重复值较多,可以考虑采用联合索引
2、为经常需要排序、分组和联合操作的字段建立索引
经常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作会浪费很多时间。
3、为常作为查询条件的字段建立索引,
如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。
注:如果经常作为条件的列,重复值特别多,可以建立联合索引
4、如果索引字段的值很长,最好使用值的前缀来索引,
例如,TEXT和BLOG类型的字段,进行全文检索,会很浪费时间。
如果只检索字段的前面的若干个 字符,这样可以提高检索速度。
----------------------------------------------------
.
.
.
B树
它跟普通的平衡二叉树的不同是,B树的每个节点可以存储多个数据,
而且每个节点不止有两个子节点,最多可以有上千个子节点。
B树中每个节点都存放着索引和数据,数据遍布整个树结构,搜索可能在非叶子节点结束,最好的情况是O(1)。
.
.
.
.
.
B+树
B+树是B树的一种变种,它与 B树 的 区别 是:
叶子节点保存了完整的索引和数据,而非叶子节点只保存索引值
叶子节点中有指向下一个叶子节点的指针
B+树更适合外部存储。由于支节点无 data 域,每个节点能索引的范围更大更精确
.
.
.
.
作业
1.课下自行百度学习什么是数据库三大范式
2.整理今日内容及博客
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY