MySQL学习笔记(长期更新)

002-字段:这么多字段类型,该怎么定义

字段评估:需要考虑存储空间和可靠性的平衡

类型选择:确保数据不会超过取值范围

注意:

精度问题:

浮点数存在精度丢失的问题,对精度要求高的字段(如价格)不要使用,原因是MySQL采用二进制存储数据,如果尾数不是0或5,就需要使用四舍五入的方式来表达,定点数类型:DECIMAL(将十进制数的整数部分和小数部分拆开,分别转换成十六进制数存储)。

主键问题

text类型长度不定,所以无法做主键,可使用char、varchar类型

时间类型

项目中使用时间类型优先考虑使用率最高的DATETIME,其占用存储空间更多、表达时间类型更为完整,取值范围更大。

总结

整数:INT。 ⼩数:DECIMAL。 字符串:TEXT。 ⽇期与时间:DATETIME。

003-表:怎么创建和修改表

约束限定了表中数据应该满足的条件。

建表时给字段设置默认值的做法,就是默认约束。在插入时,如果不明确给字段赋值,那么系统会把设置的默认值自动赋值给字段。

约束类型:

  • 默认约束:插入时如果没有指定值,则插入默认值
  • 主键约束:保证数据的唯一性
  • 外键约束:预防破坏表之间连接的行为
  • 非空约束 :字段值不能为空
  • 唯一性约束:字段值不能重复
  • 自增约束:字段在插入时自动+1,系统自动赋值

满足唯一约束的字段,可以为空值,但满足主键约束的字段,自动满足非空约束。(TINYINT、SMALLINT、MEDIUMINT、INT和BIGINT)

004-增删改查:如何操作表中的数据?

插入:插⼊⼀条部分字段数据记录是可以的,但前提是,没有赋值的字段,⼀定要让MySQL知 道如何处理,⽐如可以为空、有默认值,或者是⾃增约束字段,等等,否则,MySQL会提⽰错误的。

插入查询结果,MySQL⽀持把查询的结果插⼊到数据表中,我们可以指定字段,甚⾄是数值,插⼊到数据表中。

INSERT INTO 表名 (字段名)

SELECT 字段名或值

FROM 表名

WHERE 条件

修改:不要修改主键字段的值,主键是数据记录的唯一表示,修改主键的值可能破坏数据完整性。

Group By 作用:对查询出的数据分组,通常与聚合函数使用

  • 满足"select 字句中的列名必须为分组列或函数"

    select u.user_name, sum(et.singin)
    from user u
    left join employee_tbl et on u.id = et.user_id
    where date >= '2016-04-04 15:26:54'
    group by u.user_name;
    

    group by user_name,所以你查询列必须有user_name这个字段

  • where 必须用在group by之前

HAVING:⽤于筛选查询结果,跟WHERE类似。

FROM:其后可以跟表或查询结果(派生表/子查询),意思是将查询的数据当作一个虚拟的数据表来看待,需要使用AS关键字对派生表进行取名。

ORDER BY:对查询结果排序,ASC升序、DESC降序。

LIMIT (startIndex,length):显示部分查询结果

INSERT INTO 表名 [(字段名 [,字段名] ...)] VALUES (值的列表);
INSERT INTO 表名 (字段名)
SELECT 字段名或值
FROM 表名
WHERE 条件
DELETE FROM 表名
WHERE 条件
UPDATE 表名
SET 字段名=WHERE 条件
SELECT *|字段列表
FROM 数据源
WHERE 条件
GROUP BY 字段
HAVING 条件
ORDER BY 字段
LIMIT 起始点,⾏数

主键冲突时处理:ON DUPLICATE

INSERT INTO demo.goodsmaster
SELECT *
FROM demo.goodsmaster1 as a
ON DUPLICATE KEY UPDATE barcode = a.barcode,goodsname=a.goodsname;

当主键冲突时,唯一主键对应的数据进行更新。

05-主键:如何正确设置主键?

主键设计思路:

  • 业务字段做主键
  • 自增字段做 主键
  • 手动赋值字段做主键
  • 尽量不要使用业务字段

总结:业务字段做主键,不排除后期业务需要会出现主键一致的问题。

单系统应用可以使用自增主键不适用于多系统

手动赋值可以确保在系统中的唯一性

06-外键和连接:如何做关联查询?

多表查询:把分散在多张表中的数据查询出来。

外键(FOREIGN KEY)和连接(JOIN)将多张表关联。

设计外键

有两张表A、B通过id进行关联,如果Id在A中时主键,那么A便称为主表,B表就是从表,B表中的id字段就是外键。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DkSOLEaN-1624283827558)(…/…/AppData/Roaming/Typora/typora-user-images/image-20210602212346409.png)]

外键约束

从表中定义定义的外键指定外键字段、对应主表中的字段,MySQL会根据外键约束的定义,监控主表中数据的删除操作,如果发现要删除的主表记录,正在被从表中某条记录的外键字段所引用,MySQL会提示错误,从而保证关联数据不会丢失。

ALTER TABLE 从表名 ADD CONSTRAINT 约束名 FOREIGN KEY 字段名 REFERENCES 主表名 (字段名);
07-条件语句:WHERE与HAVING有什么不同

having:与group by连用实现对分组字段或分组计算函数进行限定

where:对数据表中的字段进行限定

having和where的区别:

  1. 如果需要连接从关联表中 获取需要的数据,WHERE是先筛选后连接,而HAVING是先连接后筛选,所以WHERE比HAVING更高效
  2. WHERE可以直接使用表中字段作为筛选条件,但不能使用分组中的计算函数作为筛选条件,HAVING必须要与GROUP BY配置使用,可以把分组计算的函数和分组字段作为筛选条件

在需要对数据进行分组统计的时候,HAVING可以完成WHERE不能完成的任务。

SQL执行顺序

(8) SELECT (9)DISTINCT<Select_list>
(1) FROM <left_table> (3) <join_type>JOIN<right_table>
(2) ON<join_condition>
(4) WHERE<where_condition>
(5) GROUP BY<group_by_list>
(6) WITH {CUBE|ROLLUP}
(7) HAVING<having_condtion>
(10) ORDER BY<order_by_list>
(11) LIMIT<limit_number>

https://img2018.cnblogs.com/blog/747662/201912/747662-20191208111950097-1165896309.png

关联表查询准则:

驱动表:多表关联时第一个被处理的表(基表),然后再使用此表的记录去关联其他的表,驱动表选择的原则:在对最终结果集没有影响的前提下,优先选择结果集最少的那张表作为驱动表。

找驱动表的规律:

LEFT JOIN 一般以左表为驱动表(RIGHT JOIN一般则是右表 ),INNER JOIN 一般以结果集少的表为驱动表,如果还觉得有疑问,则可用 EXPLAIN 来找驱动表,其结果的第一张表即是驱动表。
你以为 EXPLAIN 就一定准吗 ? 执行计划在真正执行的时候是可能改变的!

绝大多少情况下是适用的,特别是 EXPLAIN

08-聚合函数:怎么高效的进行分组统计?

LEFT(str,n):表示返回字符串str最左边的n个字符串。

select left(b.transdate, 10), c.goodsname, sum(a.quantity), sum(a.salesvalue)
from transactiondetails as a
         join transactionhead as b on a.transactionid = b.transactionid
         join goodsmaster as c on a.itemnumber = c.itemnumber
group by left(b.transdate, 10), c.goodsname
order by left(b.transdate, 10), c.goodsname;
#(transdate,10)是时间信息,datetime类型的默认格式是YYYY-MM-DD,所以长度是10

sum()、avg()、min()、max():求和、平均、最大、最小值是根据分组计算,使用时需对分组结果有准确把握。

max(column1)、min(column2)是相互独立计算的,并不会出现在同一条记录上。

count(*):统计一共有多少条记录

count(字段):统计有多少个不为空的字段值

count(*)与group by连用统计分组内有多少条数据

计算直接在数据库中执行,比在应用层面完成相同的工作,效率高很多。

09-时间函数:时间类数据,MySQL是怎么处理的?

EXTRACT() 函数用于返回日期/时间的单独部分,比如年、月、日、小时、分钟等。

select extract(hour from transdate) from transactionhead;
select extract(day from transdate) from transactionhead;
  • DATE_ADD(date,interval 表达式type):表示计算从时间点"date"开始,向前或者向后一段时间间隔的时间。"表达式"的值为时间间隔数,正数表示向后,负数表示向前,type表示时间间隔单位(比如年月日等)。
  • LAST_DAY(date):表示获取日期时间"date所在月份的最后一天的日期"。
#获取2019年12月的数据
select date_add('2020-12-10',interval  -1 year );
select date_add(DATE_ADD('2020-12-10',interval -1 year ),INTERVAL -1 month );
select last_day(date_add(DATE_ADD('2020-12-10',interval -1 year ),INTERVAL -1 month ));
select date_add(last_day(date_add(DATE_ADD('2020-12-10',interval -1 year ),INTERVAL -1 month )),interval + 1 day );
09-时间函数:时间类数据,MySQL是怎么处理的?

EXTRACT() 函数用于返回日期/时间的单独部分,比如年、月、日、小时、分钟等。

select extract(hour from transdate) from transactionhead;
select extract(day from transdate) from transactionhead;
  • DATE_ADD(date,interval 表达式type):表示计算从时间点"date"开始,向前或者向后一段时间间隔的时间。"表达式"的值为时间间隔数,正数表示向后,负数表示向前,type表示时间间隔单位(比如年月日等)。
  • LAST_DAY(date):表示获取日期时间"date所在月份的最后一天的日期"。
#获取2019年12月的数据
select date_add('2020-12-10',interval  -1 year );
select date_add(DATE_ADD('2020-12-10',interval -1 year ),INTERVAL -1 month );
select last_day(date_add(DATE_ADD('2020-12-10',interval -1 year ),INTERVAL -1 month ));
select date_add(last_day(date_add(DATE_ADD('2020-12-10',interval -1 year ),INTERVAL -1 month )),interval + 1 day );

eg:商品1周一、周三、周五分别打9、75、88折

​ 商品2周二、周四、周六分别打5、65、8折

select curdate()                                                                     as 日期,
       case dayofweek(curdate()) - 1 when 0 then 7 else dayofweek(curdate()) - 1 end as 周几,
       a.goodsname                                                                   as 商品名称,
       a.price                                                                       as 价格,
       ifnull(b.discountrate, 1)                                                     as 折扣率,
       a.price * ifnull(b.discountrate, 1)                                           as 折后价格,
       a.itemnumber
from goodsmaster as a
         left join discountrule as b on (a.itemnumber = b.itemnumber)
    #必须是一周中的某天  才满足促销条件
    and case dayofweek(curdate()) - 1 when 0 then 7 else dayofweek(curdate()) - 1 end = b.weekday;

CASE WHEN语法:

  1. 简单CASE函数

    CASE sex
    	WHEN '1' THEN '男'
    	WHEN '2' THEN '女'
    ELSE '其他' END
    
  2. CASE搜索函数

    CASE WHEN sex = '1' THEN '男'
    	 WHEN sex = '2' THEN '女'
    ELSE '其他' END
    

    Case函数只返回第一个符合条件的值,剩下的Case部分将会被自动忽略。 所以,Case函数中Else部分的默认值是NULL,这点是需要注意的地方。

10-如何进行数学计算、字符串处理的条件和判断?

floor:向下取整

ceil:向上取整

round(a,b):保留小数

concat(s1,s2…):把字符串s1,s2拼接起来,组成一个字符串

cast(表达式 as char):表示将表达式的值转换成字符串

char_length(字符串):表示获取字符串的长度

space(n):表示获取一个由N个空格组成的字符串

IF语句

IFNULL(V1,V2):表⽰如果V1的值不为空值,则返回V1,否则返回V2。
IF(表达式,V1,V2):如果表达式为真(TRUE),则返回V1,否则返回V2。

其他常用方法:

image-20210626160204090

11-索引:如何提高查询的速度?

explan中的extra:

  1. Using index condition:执行时使用了索引
  2. Using where:执行时通过Where条件进行了筛选
  3. Using MRR:使用了顺序磁盘读取的策略

使用经常被用作筛选条件的字段做索引。

如果有多个索引,而这些索引的字段同时作为筛选字段出现在查询中的时候,MySQL会选择使用最优的索引来执行查询操作。

Explain Type类型

  • system:系统表,少量数据,往往不需要进行磁盘IO
  • const:常量连接
  • eq_ref:主键索引(primary key)或者非空唯一索引(unique not null)等值扫描
  • ref:非主键非唯一索引等值扫描
  • range:范围扫描
  • index:索引树扫描
  • ALL:全表扫描(full table scan)

type由快到慢

system > const > eq_ref > ref > range > index > ALL

const索引条件:

  • 命中主键(primary key)或者唯一索引(unique)
  • 被连接的部分是一个常量值(const)

eq-ref索引条件:

  • 联表(join)查询
  • 命中主键(promary key)或者非空唯一索引(unique not null)
  • 等值连接
create table user_balance (
  uid int primary key,
  balance int
)engine=innodb;

#表的主键中加入索引
explain
select *
from user
         left join user_balance on user.id = user_balance.uid
where user.id = user_balance.uid;

等值连接:

select *
from Table_1 t1, Table_2 t2
where t1.C = t2.D

ref索引条件:

  • 命中非唯一索引
  • 连表查询

range:

  • 范围查询(between、in、>,>=,<,<=)

index:

  • 需要扫描索引上的全部数据,仅比全表扫描快一点

ALL:

  • 未命中索引,全表扫描

组合索引的原理

组合索引的多个和字段是有序的,遵循左对齐的原则。

创建组合索引,排序方式:branchnumber、cashiernumber和itemnumber,因此筛选条件也要从左向右的原则,如果中断则后面的条件也无法使用索引。

branchnumber = 11 AND cashiernumber = 1 AND itemnumber = 100,包含了从左到右所有字段,所以可以最大限度使用索引。加入把条件换成cashiernumber = 1 AND itemnumber = 100,最左边的字段无法命中组合索引最左侧字段条件,所以无法使用组合索引。

如果筛选是一个范围,若无法精确定位,也相当于中断。⽐如“branchnumber > 10 AND cashiernumber = 1 AND itemnumber = 100”这个条件,只能⽤到组合索引中branchnumber>10的 部分,后⾯的索引就都⽤不上了。

12-事务:怎么确保关联操作正确执行?

正确使用事务,保证关联操作同时成功或同时失败回滚。

START TRANSACTION;
SELECT/UPDATE/CREATE/DELETE SQL语句
commit ;
rollback;
  • START TRANSACTION/BEGIN:表示开始事务,该语句后的SQL都属于当前事务
  • COMMIT:提交事务,执行当前事务的全部操作,使更改的数据生效
  • ROLLBACK:表示回滚当前事务的操作,取消对数据的修改

事务的四个特性:

  • 原子性(atomicity):表示事务的操作要么全部执行,要么全部不执行。一个整体,不能被打断。
  • 一致性(consistency):表示数据的完整性不会因为事务的执行而受到破坏。
  • 隔离性(cisolation):表示多个事务同时执行的时候,不互相打扰。不同的隔离级别,相互独立的程度不同。
  • 持久性(durability):表示事务对数据库的修改是永久有效的。
START TRANSACTION;
INSERT INTO mytrans
values (1, 1, 5);
UPDATE inventory
SET invquantity=invquantity - 5
where itemnumber = 1;
commit;
rollback;

MySQL并不会处理SQL语句中的错误,所以如果事务中的某SQL执行出现错误后提交会出现事务不一致的问题,如mytrans在插入时出现错误,inventory表执行成功,库存字段数据-5,这样会导致数据不一致的问题。为解决该问题,可以使用rollback进行回滚。

rollback回滚:在一个事务中,如果有一个SQL语句执行失败,那么当前事务便不会提交,相当于当前事务中的SQL并没有执行。

当操作多表时,未防止数据不一致的问题,保持操作的原子性,需要事务回滚rollback

如何做好事务的隔离性?

会员张三是储值会员,他的会员卡⾥有100元。 张三⽤会员卡到⻔店消费100元,他爱⼈⽤他的会员卡在⽹上消费100元。 张三在⻔店消费结算的时候,开启了⼀个事务A,包括这样3个操作: 1. 读取卡内⾦额为100; 2. 更新卡内⾦额为0; 3. 插⼊⼀条销售流⽔。 张三的爱⼈在⽹上购物,开启了⼀个事务B,也来读取卡内⾦额。如果B读取卡内⾦额的操作,发⽣在A更新 卡内⾦额之后,并且在插⼊销售流⽔之前,那么B读出的⾦额应该是多少呢?如果B读出0元,那么,A有可 能由于后⾯的操作失败⽽回滚。因此,B可能会读到⼀条错误信息,⽽导致本来可以成功的交易失败。有什 么办法可以解决这个问题呢?

MySQL的锁:MySQL可以把A中被修改过而且还没有提交的数据锁住,让B处于等待状态,一直到A提交完成,或者失败回滚再释放锁,允许B读取这个数据,这样就可以防止A回滚导致B读取错误的可能。

锁的使用方式不同,隔离的程度也不同,隔离程度越高,消耗的资源越多。

  1. READ UNCOMMITTED:可以读取事务中还未提交的被修改的数据
  2. READ COMMITTED:只能读取事务中已经提交的被修改的数据
  3. REPEATABLE READ:表示一个事务中,对一个数据读取的值,永远跟第一次读取的值一致,不受其他事务中数据操作的影响。(默认)
  4. SERIALIZABLE:任何一个事务,一旦对某一个数据进行了任何操作,那么一直到这个事务结束,MySQL都会把这个数据锁住,禁止其他事务对这个数据进行任何操作

对于核心数据的更改操作,需要较高的隔离等级。

14-视图:如何简化查询?
create view viewName as select * from --查询语句;

子查询:嵌套在另一个查询中的查询。

派生表:如果我们在查询中把子查询的结果作为一个表来使用,这个表就是派生表。

子查询按返回结果集进行分类:

  • 表子查询:返回的结果是一个行的集合,N行N列,(N>=1)。表子查询经常用于父查询的FROM子句中。
  • 行子查询:返回的结果是一个列的集合,一行N列,(N>=1)。行子查询常用于父查询的FROM字句和WHERE字句中。
  • 列子查询:返回的 结果是一个行的集合,N行一列,(N>=1)。
  • 标量子查询:返回的结果集是一个标量集合,一行一列。

子查询按返回结果集的调用方法进行分类:

  • where型子查询:内层查询结果当作外层查询的条件
  • from型子查询:内层查询的结果供外层再次查询
  • exists型子查询:把外层查询结果拿到内层,看内层查询是否成立

使用子查询规则:

  1. 一个子查询必须放在圆括号内
  2. 将子查询放在比较条件的右边以增加可读性
  3. 子查询不包含ORDER BY字句,对一个SELECT语句只能有一个ORDER BY字句,如果使用的话,只能放在主SELECT语句的最后。
  4. 子查询中可以使用两种比较条件:单行运算符(>, =, >=, <, <>, <=)和多行运算符(IN,ANY,ALL)。
15-存储过程:如何提高程序的性能和安全性?
16-游标:对于数据集中的记录,

该怎么逐条处理?

游标:对结果集中的每一条记录进行定位,并对指向的记录中的数据进行操作的数据结构。

18-权限管理:如何控制数据库访问,消除安全隐患?

角色是MySQL8.0中引入的新功能,相当于一个权限的集合,作用是方便管理拥有相同权限的用户,类似于RBAC权限框架。
恰当的权限设定,可以确保数据的安全性。

创建角色

create role 'roleName'
#可省略主机,表示该账号可以在任何主机上登录
@'localhost';

赋予角色权限

GRANT permissions on tableName TO roleName;

#查看是否添加角色成功
show GRANTS FOR 'roleName'

删除角色

DROP ROLE 'roleName';

**注意:**MySQL中创建了角色后,默认都是没有被激活的。

19-日志(上):系统出现问题,如何及时发现?
通用查询日志

通用查询日志记录了所有用户的连接开始时间和截至时间,以及发给MySQL数据库服务器的所有SQL指令。

通过该命令可以还原具体场景,定位问题。

#查询日志记录
show variables like '%general_log%';
general_log : OFF
#说明通用日志是在关闭状态
general_log_file:是日志存放位置
#开启日志查询
set GLOBAL general_log = 'ON';
#修改日志存放位置
set @@global.general_log_file = 'pathValue'

image-20210710154409436

在ssh中查看MySQL执行语句:

tail -f /var/lib/mysql/iZbp1etc398n6cac736htxZ.log

image-20210710155250853

慢查询日志

慢查询日志是用来记录执行时间超过指定时长的查询。

当数据库服务器发生阻塞、运行变慢的时候,检查一下慢查询日志,优化慢查询。

MySQL配置开启慢查询

vim /etc/my.cnf
#插入以下数据
slow_query_log=TRUE      											#开启慢日志
slow_query_log_file=/home/mysql02/mysql/logs/slow_query_log.txt    	#日志存放位置
long_query_time=5													#超时时间5秒(超过5秒就会被记录下来)

image-20210710162632209

restart mysqld.service

long_query_time还可以通过set global long_query_time=xxx来设置,省略单位秒,设置完成后重启MySQL服务。

查询扫描过的最少记录数

image-20210710163504851

慢查询判断条件
  1. 查询执行时间
  2. 扫描过的最少记录数

当前扫描过的最少记录数是0,long_query_time是5,意思是只要查询超过5秒,哪怕一个记录都没有扫描过,都要被记录到慢查询中。

错误日志

错误日志记录了MySQL服务启动、停止的时间,以及系统启动、运行和停止过程中的诊断信息,包括错误、警告和提示。

二进制日志

主要记录数据的更新事件。

查看当前正在写入的二进制日志:

show master status;

查看所有二进制日志的SQL语句

show binary logs;

用二进制日志恢复数据(mysqlbinlog)

mysqlbinlog -start-positon=xxx -end postion=yyy 二进制文件名 | mysql -u -p;

执⾏⼆进制⽇志中从位置xxx开始,到yyy截⽌的所有数据更新操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值