day49 python操作MySQL 索引与慢查询优化

上周内容回顾

查询关键字:

where: 筛选  也可以说是过滤  分组之前使用
group by:分组
having: 过滤  分组之后使用
order by:排序  升序降序 
distinct: 去重
limit:限制分页 分页
regepx:正则

多表查询思路:

两种 一种是子查询 一种是连表查询
1.子查询就是 将一条SQL语句的查询结果括号括起来当做另外2.一条SQL语句的查询条件
连表查询就是将多张表拼接到一起 之后基于单表查询操作获取数据、
  inner join		内连接
  left join			左连接
  right join		右连接
  union					全连接
上述两种方式  实际工作中  可能都是混合使用·····

navica可视化软件

简介:
Navicat 是一套快速、可靠并价格相宜的数据库管理工具,专为简化数据库的管理及降低系统管理成本而设。它的设计符合 数据库管理员、开发人员及中小企业的需要。Navicat 是以直觉化的 图形用户界面而建的,让你可以以安全并且简单的方式创建、组织、访问并共用信息。 Navicat提供多达 7 种语言供客户选择,被公认为全球最受欢迎的数据库前端用户界面工具。 它可以用来对本机或远程的 MySQL、SQL Server、SQLite、Oracle 及 PostgreSQL 数据库进行管理及开发。 Navicat适用于三种平台 - Microsoft Windows、Mac OS X 及 Linux 。它可以让用户连接到任何本机或远程服务器、提供一些实用的数据库工具如数据模型、数据传输、 数据同步 、结构同步、导入、导出、备份、还原、报表创建工具及计划以协助管理数据
能够充当数据库客户端的可视化软件有很多
针对MySQL使用频率较高的软件就是navicat
针对MongoDB使用频率较高的软件是ROBO3T
# 1.链接
# 2.创建库、表、记录、外键
# 3.逆向数据库到模型
# 4.SQL文件
# 5.自定义查询
MySQL注释语法:
#、--
涉及多表的一些查询  记得一步一步来写复杂的SQL语句也不要想着一步到位

python操作MySQL

python操作mysql的方法:首先输入命令行pip install pymysql进行导包;然后打开pycham编辑器创建python文件,输入import pymysql进行导包;最后通过游标来操作数据库。
1.首先在虚拟机上或者本地cmd命令行下输入命令行pip install pymysql进行导包。初次导包时间比较久,耐心等待数据加载完成即可。
2.打开pycham编辑器创建一个python文件,然后输入import pymysql进行导包,接着建立连接数据库存和python连接。con =pymysql.connect(
host = '127.0.0.1',
port =3306,
user = 'mango',
password = '123456',
db = 'test1',
charset = 'utf8'
)

注:其中host为本机ip地址,port是端口号,user是数据库用户名,password为数据库密码。db是指操作的数据名,charset是编码格式。
3.将python和数据库连接搭建好之后,定义一个游标,通过游标来操作数据库,定义方法如下:cur = con.cursor()
4.通过cur.execute()来执行sql语句,例如查询数据库存中的所有表格,具体操作如下:row = cur.execute('show tables')
5.通过上面操作我们可以看到数据库中有多少张表,那么如何取出表里的数据呢。
这个时候就要用到cur.fetchall()来取数据了。具体操作如下:all = cur.fetchall()
print(all)
6.学会了简单的执行sql语句和取数据后,我们可以开始通过python向数据库中创建表格了。创建一个变量用来接收sql语句,使用三引号便于编辑sql语句的时候可以自由换行。具体操作如下:table = 
create table test0(
id INT,
name CHAR(10)
)
cur.execute(table)
7、创建好表格之后,我们可以向表格里插入数据,这个时候需要用到conn.commit()进行提交事务,否则数据不会写入到数据库。具体操作如下:cur.execute(insert test0 value(1,'小吕'))
con.commit()
最后我们在结束继续编程的时候需要将游标关闭并且断开连接。cur.close()关闭游标
con.close()关闭连接
import pymysql

# 1.链接服务端
conn_obj = pymysql.connect(
    host='127.0.0.1',  # MySQL服务端的IP地址
    port=3306,  # MySQL默认PORT地址(端口号)
    user='root',  # 用户名
    password='输入自己的密码',  # 密码  也可以简写 passwd  输入自己的密码即可
    database='库名称',  # 库名称  也可以简写 db
    charset='utf8'  # 字符编码 千万不要加杠utf-8
)  # 要善于查看源码获取信息
# 2.产生获取命令的游标对象
cursor = conn_obj.cursor(
    cursor=pymysql.cursors.DictCursor
)  # 括号内不写参数 数据是元组要元组 不够精确 添加参数则会将数据处理成字典
# 3.编写SQL语句
# sql1 = 'show tables'
sql1 = 'select * from teacher'  # SQL语句会被高亮显示 不用惊慌
# sql1 = 'select * from score'  # SQL语句会被高亮显示 不用惊慌
# 4.执行SQL语句
'''execute执行sql语句 会自动帮你加分号结束符 如果你没有写的话'''
affect_rows = cursor.execute(sql1)
print(affect_rows)  # 执行SQL语句之后受影响的行数
# 5.获取结果
# print(cursor.fetchall())  # 获取结果集中所有
# print(cursor.fetchone())  # 获取结果集中的第一个
# print(cursor.fetchmany(3))  # 获取结果集中的指定个数
# print(cursor.fetchmany(3))  # 获取结果集中的指定个数
'''控制结果集中光标的移动'''
# print(cursor.fetchone())
# cursor.scroll(1, 'relative')  # 相对于当前位置往后移动一个单位
print(cursor.fetchall())
cursor.scroll(1, 'absolute')  # 相对于起始位置往后移动一个单位
print(cursor.fetchall())

SQL注入问题

# 写正确的用户名错误的密码也可以登录
	用户名:jason' -- jbjbjbjbjbjbjbjbjbjbjb
  密码:直接回车
# 用户名和密码都不需要也可以登录
	用户名:xxx' or 1=1 -- asasasasasasasasa
xxx or 1=1(恒成立)
  密码:直接回车
"""上述现象就是典型的SQL注入问题"""
其实这种典型的注入问题也不过就是利用了语法上的漏洞  不过就是利用注释把后面的一些代码注释而已注释之后的代码也无须执行
解决中问题也很简单  以为注释符号属于特殊符号所以就是想办法过滤掉特殊符号
	execute方法自带校验SQL注入问题 自动处理特殊符号
  ps:设计到敏感数据的拼接 全部交给execute方法即可!!!
    sql = "select * from userinfo where name=%s and password=%s;"
	cursor.execute(sql, (name, password))
	  
"""
execute方法补充(了解)
	批量插入数据
		sql = 'insert into userinfo(name,password) values(%s,%s)'
		cursor.executemany(sql,[('tom',123),('lavin',321),('pony',333)])
import pymysql


conn_obj = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='jason123',
    database='jp_05',
    charset='utf8'
)
cursor = conn_obj.cursor(
    cursor=pymysql.cursors.DictCursor
)
# 1.获取用户名和密码
name = input('请输入您的用户名>>>:').strip()
password = input('请输入您的密码>>>:').strip()
# 2.拼接查询语句
sql = "select * from userinfo where name=%s and password=%s;"
# 3.执行SQL语句
cursor.execute(sql, (name, password))
res = cursor.fetchall()
if res:
    print('登录成功')
else:
    print('用户名或密码错误')

二次确认

数据的增删改查四个操作是有轻重之分的所谓的轻重之分个人理解就是危险程度之分
首先增删改查之中只有查不会影响数据的一个本质你只是去看看肯定没有问题也不会去动数据,而改动数据,增加数据,删除数据,这三个首先就是影响了数据的一个本质所以pymysql
针对这种会改变数据本质的操作会进行二次确认 如果是不确认则不会影响数据的一个本质
import pymysql


conn_obj = pymysql.connect(
    host='127.0.0.1',MySQL服务端的IP地址
    port=3306,默认MySQLPORT地址端口号
    user='root',username 用户名字
    password='密码',
    database='数据库名',  可以简写为db
    charset='utf8',  字符编码
    autocommit=True  # 自动二次确认
)
cursor = conn_obj.cursor(
    cursor=pymysql.cursors.DictCursor
)
# sql = 'select * from userinfo'  # 查数据 ok
# sql = 'insert into userinfo(name,password) values("jack111",123)'  # 增数据  没有效果
sql = 'insert into userinfo(name,password) values(%s,%s)'  # 增数据  没有效果
# sql = 'update userinfo set name="jasonNB" where id=1'  # 改数据  没有效果
# sql = 'delete from userinfo where id=5'  # 删数据  没有效果
"""
数据的增删改查四个操作是有轻重之分的
    查                            不会影响真正的数据 重要程度最低
    增、改、删                      都会影响真正的数据 重要程度较高
pymysql针对增、改、删三个操作 都设置了二次确认 如果不确认则不会真正影响数据库
"""
# affect_row = cursor.execute(sql)
affect_row = cursor.executemany(sql,[('tom',123),('lavin',321),('pony',333)])
# conn_obj.commit()  # 二次确认
print(affect_row)
print(cursor.fetchall())

修改表SQL语句补充

修改表名字:
alter table emp rename emp1;
mysql> show tables;
+----------------+
| Tables_in_jp04 |
+----------------+
| emp1           |
| zqht1          |
+----------------+
添加字段 默认尾部追加;alter table t2 add pwd int;可以设定默认值防止报错
指定追加:alter table t2 add tid int after name;
指定头部添加:alter table t2 add nid int first;
修改字段: change(名字类型都可)/modify(只能改类型不能改名字)
alter table t2 change pwd password tinyint;
alter table t2 modify password samlliny;
删除字段: alter table t2 drop nid;

视图

视图是由数据库中的一个表或多个表导出的虚拟表,是一种虚拟存在的表,方便用户对数据的操作。

1.1 视图的概念
视图是一个虚拟表,是从数据库中一个或多个表中导出来的表,其内容由查询定义。同真实表一样,视图包含一系列带有名称的列和行数据。但是,数据库中只存放了视图的定义,而并没有存放视图中的数据。这些数据存放在原来的表中。使用视图查询数据时,数据库系统会从原来的表中取出对应的数据。因此,视图中的数据是依赖于原来的表中的数据的。一旦表中的数据发生改变,显示在视图中的数据也会发生改变。

"""
视图的概念 
	通过SQL语句的执行得到的一张虚拟表 保存下来之后就称之为'视图'
视图的作用
	如果需要频繁的使用一张虚拟表 可以考虑制作成视图 降低操作难度
		eg: emp与dep表拼接
视图的制作view
	create view 视图名 as sql语句
"""
# 视图虽然看似很好用 但是会造成表的混乱 毕竟视图不是真正的数据源
# 视图只能用于数据的查询 不能做增、删、改的操作 可能会影响原始数据(视图里面的数据是直接来源于原始表 而不是拷贝一份)

触发器

   触发器(trigger):监视某种情况,并触发某种操作,它是提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,例如当对一个表进行操作( insert,delete, update)时就会激活它执行。

    触发器经常用于加强数据的完整性约束和业务规则等。 触发器创建语法四要素:
        1.监视地点(table)
        2.监视事件(insert/update/delete) 
        3.触发时间(after/before) 
        4.触发事件(insert/update/delete)
  其中:trigger_time是触发器的触发事件,可以为before(在检查约束前触发)或after(在检查约束后触发);trigger_event是触发器的触发事件,包括insert、update和delete,需注意对同一个表相同触发时间的相同触发事件,只能定义一个触发器;可以使用old和new来引用触发器中发生变化的记录内容。、
触发器作用
	专门针对表数据的操作 定制个性化配套功能
触发器种类
	表数据新增之前、新增之后
	表数据修改之前、修改之后
	表数据删除之前、删除之后
触发器创建
	create trigger 触发器名字 before/after insert/update/delete
	on 表名 for each row
		begin
			SQL语句
		end;
触发器的名字一般情况下建议采用下列布局形式
		tri_after_insert_t1
		tri_before_update_t2
		tri_before_delete_t3

1.先创建两张表
  	# 案例
    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') #0代表执行失败
    );

    CREATE TABLE errlog (
        id INT PRIMARY KEY auto_increment,
        err_cmd CHAR (64),
        err_time datetime
    );
  2.需求:cmd表插入数据的success如果值为no 则去errlog表中插入一条记录
    delimiter $$  # 将mysql默认的结束符由;换成$$
    create trigger tri_after_insert_cmd after insert on cmd for each row
    begin
        if NEW.success = 'no' then  # 新记录都会被MySQL封装成NEW对象
            insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
        end if;
    end $$
    delimiter ;  # 结束之后记得再改回来,不然后面结束符就都是$$了
  3.仅仅往cmd表中插入数据
  	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');
  4.触发器其他补充
  	查看当前库下所有的触发器信息
    	show triggers\G;
    删除当前库下指定的触发器信息
    	drop trigger 触发器名称;

具体案例(了解)

首先我们来创建两张表:

#商品表

create table g

(

  id int primary key auto_increment,

  name varchar(20),

  num int

);

#订单表

create table o

(

  oid int primary key auto_increment,

   gid int,

     much int

);

insert into g(name,num) values('商品1',10),('商品2',10),('商品3',10);

 

如果我们在没使用触发器之前:假设我们现在卖了3个商品1,我们需要做两件事

1.往订单表插入一条记录

insert into o(gid,much) values(1,3);

2.更新商品表商品1的剩余数量

update g set num=num-3 where id=1;

 

现在,我们来创建一个触发器:

需要先执行该语句:delimiter $(意思是告诉mysql语句的结尾换成以$结束)

create trigger tg1
after insert on o
for each row 
begin
update g set num=num-3 where id=1;
end$

这时候我们只要执行:

insert into o(gid,much) values(1,3)$

会发现商品1的数量变为7了,说明在我们插入一条订单的时候,触发器自动帮我们做了更新操作。

 

但现在会有一个问题,因为我们触发器里面num和id都是写死的,所以不管我们买哪个商品,最终更新的都是商品1的数量。比如:我们往订单表再插入一条记录:insert into o(gid,much) values(2,3),执行完后会发现商品1的数量变4了,而商品2的数量没变,这样显然不是我们想要的结果。我们需要改改我们之前创建的触发器。

我们如何在触发器引用行的值,也就是说我们要得到我们新插入的订单记录中的gid或much的值。

对于insert而言,新插入的行用new来表示,行中的每一列的值用new.列名来表示。

所以现在我们可以这样来改我们的触发器

create trigger tg2
after insert on o
for each row 
begin
update g set num=num-new.much where id=new.gid;(注意此处和第一个触发器的不同)
end$

第二个触发器创建完毕,我们先把第一个触发器删掉

drop trigger tg1$

再来测试一下,插入一条订单记录:insert into o(gid,much) values(2,3)$

执行完发现商品2的数量变为7了,现在就对了。

 

现在还存在两种情况:

1.当用户撤销一个订单的时候,我们这边直接删除一个订单,我们是不是需要把对应的商品数量再加回去呢?

2.当用户修改一个订单的数量时,我们触发器修改怎么写?

我们先分析一下第一种情况:

监视地点:o表

监视事件:delete

触发时间:after

触发事件:update

对于delete而言:原本有一行,后来被删除,想引用被删除的这一行,用old来表示,old.列名可以引用被删除的行的值。

那我们的触发器就该这样写:

create trigger tg3

after delete on o

for each row

begin

update g set num = num + old.much where id = old.gid;(注意这边的变化)

end$

创建完毕。

再执行delete from o where oid = 2$

会发现商品2的数量又变为10了。

 

第二种情况:

监视地点:o表

监视事件:update

触发时间:after

触发事件:update

对于update而言:被修改的行,修改前的数据,用old来表示,old.列名引用被修改之前行中的值;

修改的后的数据,用new来表示,new.列名引用被修改之后行中的值。

那我们的触发器就该这样写:

create trigger tg4

after update on o

for each row

begin

update g set num = num+old.much-new.much where id = old/new.gid;

end$

先把旧的数量恢复再减去新的数量就是修改后的数量了。

我们来测试下:先把商品表和订单表的数据都清掉,易于测试。

假设我们往商品表插入三个商品,数量都是10,

买3个商品1:insert into o(gid,much) values(1,3)$  

这时候商品1的数量变为7;

我们再修改插入的订单记录: update o set much = 5 where oid = 1$

我们变为买5个商品1,这时候再查询商品表就会发现商品1的数量只剩5了,说明我们的触发器发挥作用了。

 

事务

一、MySQL事务的概念
● 事务是一种机制、一个操作序列,包含了一组数据库操作命令,并且把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行。
● 事务是一个不可分割的工作逻辑单元,在数据库系统上执行并发操作时,事务是最小的控制单元。
● 事务适用于多用户同时操作的数据库系统的场景,如银行、保险公司及证券交易系统等等。
● 事务通过事务的整体性以保证数据的一致性。

说白了,所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。

二、事务的ACID特点
ACID,是指在可靠数据库管理系统(DBMS)中,事务(transaction)应该具有的四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。这是可靠数据库所应具备的几个特性。

1、原子性
指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生。

事务是一个完整的操作,事务的各元素是不可分的。
事务中的所有元素必须作为一个整体提交或回滚。
如果事务中的任何元素失败,则整个事务将失败。
案例:
A给B转帐100元钱的时候只执行了扣款语句,就提交了,此时如果突然断电,A账号已经发生了扣款,B账号却没收到加款,在生活中就会引起纠纷。这种情况就需要事务的原子性来保证事务要么都执行,要么就都不执行。

2、一致性
指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏

当事务完成时,数据必须处于一致状态。
在事务开始前,数据库中存储的数据处于一致状态。
在正在进行的事务中,数据可能处于不一致的状态。
当事务成功完成时,数据必须再次回到已知的一致状态。
案例:
对银行转帐事务,不管事务成功还是失败,应该保证事务结束后表中A和B的存款总额跟事务执行前一致。

3、隔离性
指在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。

对数据进行修改的所有并发事务是彼此隔离的,表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。
修改数据的事务可在另一个使用相同数据的事务开始之前访问
这些数据,或者在另一个使用相同数据的事务结束之后访问这些数据
4、持久性
在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。

指不管系统是否发生故障,事务处理的结果都是永久的。
一旦事务被提交,事务的效果会被永久地保留在数据库中。
三、事务之间的相互影响
1、脏读:一个事务读取了另一个事务未提交的数据,而这个数据是有可能回滚的。

2、不可重复读:一个事务内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。

3、幻读:一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,另一个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,操作前一个事务的用户会发现表中还有没有修改的数据行,就好象发生了幻觉一样。

4、丢失更新:两个事务同时读取同一条记录,A先修改记录,B也修改记录(B不知道A修改过),B提交数据后B的修改结果覆盖了A的修改结果。
改变事务提交方式

SET AUTOCOMMIT=1(默认)      #自动提交事务

SET AUTOCOMMIT=0                #手动提交事务

如果想多条sql放在一个事务中执行,则需要使用如下语句。

START TRANSACTION              #开启事务

COMMIT                                      #提交事务

ROLLBACK                                #回滚事务

SAVE  POINT 回滚点                  #设置回滚点

具体使用

1.创建表及录入数据
  	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);
查询创建好的表
select *from user;
+----+-------+---------+
| id | name  | balance |
+----+-------+---------+
|  1 | jason |    1000 |
|  2 | kevin |    1000 |
|  3 | tank  |    1000 |
+----+-------+---------+
2.事务操作
  	开启一个事务的操作
start transaction;
 编写SQL语句(同属于一个事务)
    	update user set balance=900 where name='jason';
			update user set balance=1010 where name='kevin'; 
			update user set balance=1090 where name='tank';

查询表结果:
select *from user;
+----+-------+---------+
| id | name  | balance |
+----+-------+---------+
|  1 | jason |     900 |
|  2 | kevin |    1010 |
|  3 | tank  |    1090 |
+----+-------+---------+
  事务回滚(返回执行事务操作之前的数据库状态)
    	rollback;  # 执行完回滚之后 事务自动结束
            事务确认(执行完事务的主动操作之后 确认无误之后 需要执行确认命令)
rollback;
Query OK, 0 rows affected (0.01 sec)

mysql> select *from user;
+----+-------+---------+
| id | name  | balance |
+----+-------+---------+
|  1 | jason |    1000 |
|  2 | kevin |    1000 |
|  3 | tank  |    1000 |
+----+-------+---------+
commit;  # 执行完确认提交之后 无法回滚 事务自动结束
可以说是commit 执行之后rollback失效  

储存过程

类似于python中的自定义函数

# 相当于定义函数
delimiter $$
create procedure p1()
begin
	select * from user;
end $$
delimiter ;

# 相当于调用函数
call p1()

"""
类似于有参函数
  delimiter $$
  create procedure p1(
      in m int,  # in表示这个参数必须只能是传入不能被返回出去
      in n int,  
      out res int  # out表示这个参数可以被返回出去,还有一个inout表示即可以传入也可以被返回出去
  )
  begin
      select tname from userinfo where id > m and id < n;
      set res=0;  # 用来标志存储过程是否执行
  end $$
  delimiter ;

# 针对res需要先提前定义
  set @res=10;  定义
  select @res;  查看
  call p1(1,5,@res)  调用
  select @res  查看

查看存储过程具体信息
	show create procedure pro1;
查看所有存储过程
	show procedure status;
删除存储过程
	drop procedure pro1;
"""

函数

MYSQL 内置函数函数只能在sql语句中使用
"ps:可以通过help 函数名    查看帮助信息!"
# 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');
创建结果:
select *from blog;
+----+---------+---------------------+
| id | NAME    | sub_time            |
+----+---------+---------------------+
|  1 | 第1篇   | 2015-03-01 11:31:21 |
|  2 | 第2篇   | 2015-03-11 16:31:21 |
|  3 | 第3篇   | 2016-07-01 10:21:31 |
|  4 | 第4篇   | 2016-07-22 09:23:21 |
|  5 | 第5篇   | 2016-07-23 10:11:11 |
|  6 | 第6篇   | 2016-07-25 11:21:31 |
|  7 | 第7篇   | 2017-03-01 15:33:21 |
|  8 | 第8篇   | 2017-03-01 17:32:21 |
|  9 | 第9篇   | 2017-03-01 18:31:21 |
+----+---------+---------------------+
查寻结果
 select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');
+-------------------------------+-----------+
| date_format(sub_time,'%Y-%m') | count(id) |
+-------------------------------+-----------+
| 2015-03                       |         2 |
| 2016-07                       |         4 |
| 2017-03                       |         3 |
+-------------------------------+-----------+
1.where Date(sub_time) = '2015-03-01'
2.where Year(sub_time)=2016 AND Month(sub_time)=07;
# 更多日期处理相关函数 
	adddate	增加一个日期 
	addtime	增加一个时间
	datediff	计算两个日期差值

流程控制

# python if判断
	if 条件:
    子代码
  elif 条件:
    子代码
  else:
    子代码
# js if判断
	if(条件){
    子代码
  }else if(条件){
    子代码
  }else{
    子代码
  }
# MySQL if判断
	if 条件 then
        子代码
  elseif 条件 then
        子代码
  else
        子代码
  end if;
  
# MySQL while循环
	DECLARE num INT ;
  SET num = 0 ;
  WHILE num < 10 DO
    SELECT num ;
    SET num = num + 1 ;
  END WHILE ;

索引

索引是什么
官方介绍索引是帮助MySQL高效获取数据的数据结构。更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度。

一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往是存储在磁盘上的文件中的(可能存储在单独的索引文件中,也可能和数据一起存储在数据文件中)。

我们通常所说的索引,包括聚集索引、覆盖索引、组合索引、前缀索引、唯一索引等,没有特别说明,默认都是使用B+树结构组织(多路搜索树,并不一定是二叉的)的索引。

索引的优势和劣势
优势:

可以提高数据检索的效率,降低数据库的IO成本,类似于书的目录。

通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗。

被索引的列会自动进行排序,包括【单列索引】和【组合索引】,只是组合索引的排序要复杂一些。
如果按照索引列的顺序进行排序,对应order by语句来说,效率就会提高很多。
劣势:

索引会占据磁盘空间

索引虽然会提高查询效率,但是会降低更新表的效率。比如每次对表进行增删改操作,MySQL不仅要保存数据,还有保存或者更新对应的索引文件。

索引类型
主键索引
索引列中的值必须是唯一的,不允许有空值。

普通索引
MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值。

唯一索引
索引列中的值必须是唯一的,但是允许为空值。

全文索引
只能在文本类型CHAR,VARCHAR,TEXT类型字段上创建全文索引。字段长度比较大时,如果创建普通索引,在进行like模糊查询时效率比较低,这时可以创建全文索引。 MyISAM和InnoDB中都可以使用全文索引。

空间索引
MySQL在5.7之后的版本支持了空间索引,而且支持OpenGIS几何数据模型。MySQL在空间索引这方面遵循OpenGIS几何数据模型规则。

前缀索引
在文本类型如CHAR,VARCHAR,TEXT类列上创建索引时,可以指定索引列的长度,但是数值类型不能指定。

其他(按照索引列数量分类)
单列索引

组合索引

组合索引的使用,需要遵循最左前缀匹配原则(最左匹配原则)。一般情况下在条件允许的情况下使用组合索引替代多个单列索引使用。
# 索引就是一种数据结构
	类似于书的目录。意味着以后再查数据应该先找目录再找数据,而不是用翻页的方式查询数据
  
索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构
	primary key 	主键
  unique	key		唯一键
  index 	key   索引键
上面三种key前两种除了有加速查询的效果之外还有额外的约束条件(primary key:非空且唯一,unique key:唯一),而index key没有任何约束功能只会帮你加速查询
# ps:foreign key不是用来加速查询用的,不在我们研究范围之内

# 索引的基本用法
id	name	pwd		post_comment  addr  age 
	基于id查找数据很快 但是基于addr查找数据就很慢 
  	解决的措施可以是给addr添加索引
'''索引虽然好用 但是不能无限制的创建!!!'''
索引的基本用法
id	name	pwd		post_comment  addr  age 
	基于id查找数据很快 但是基于addr查找数据就很慢 
  	解决的措施可以是给addr添加索引
'''索引虽然好用 但是不能无限制的创建!!!'''
索引的影响:
在表中有大量数据的前提下,创建索引速度会很慢
在索引创建完毕后,对表的查询性能会大幅度提升,但是写的性能会降低
索引的底层数据结构是b+树
B+树:只有叶子节点才会存储数据,非叶子节点至存储键值。叶子节点之间使用双向指针连接,最底层的叶子节点形成了一个双向有序链表。

img

  	上述结构都是为了更好的基于树查找到相应的数据

只有叶子结点存放真实数据,根和树枝节点存的仅仅是虚拟数据
查询次数由树的层级决定,层级越低次数越少
一个磁盘块儿的大小是一定的,那也就意味着能存的数据量是一定的。如何保证树的层级最低呢?一个磁盘块儿存放占用空间比较小的数据项
思考我们应该给我们一张表里面的什么字段字段建立索引能够降低树的层级高度>>> 主键id字段

"""
聚集索引(primary key)
辅助索引(unique key,index key)
	查询数据的时候不可能都是用id作为筛选条件,也可能会用name,password等字段信息,那么这个时候就无法利用到聚集索引的加速查询效果。就需要给其他字段建立索引,这些索引就叫辅助索引

叶子结点存放的是辅助索引字段对应的那条记录的主键的值(比如:按照name字段创建索引,那么叶子节点存放的是:{name对应的值:name所在的那条记录的主键值})
数据查找 如果一开始使用的是辅助索引 那么还需要使用聚焦索引才可以获取到真实数据

覆盖索引:只在辅助索引的叶子节点中就已经找到了所有我们想要的数据
	select name from user where name='jason';
非覆盖索引:虽然查询的时候命中了索引字段name,但是要查的是age字段,所以还需要利用主键才去查找
	select age from user where name='jason';
"""
posted @   文质彬彬赵其辉  阅读(274)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示