MySQL操作

MySQL相关操作

DDL: 操作数据库、表

1. 操作数据库: CRUD
	1. C(Create): 创建
      	* 创建数据库:
        		create database 数据库名称;
      	* 创建数据库, 并设置为字符集为utf-8:
        		create database 数据库名称 charset=utf8(编码格式);
      	* 判断数据库是否存在, 不存在才创建:
        		create database if not exists 数据库名;
      	* 判断数据库是否存在, 不存在才创建, 并指定字符集为utf-8:
        		create database if not exists 数据库名称 charset=utf8;
        		
        		
    2. R(Retrieve): 查询
      	* 查询所有的数据库名称:
        		show databases;
      	* 查询某个数据库的创建语句:
        		show create database 数据库的名称;
        		
        		
    3. U(Update): 修改
      	* 修改数据库的字符集
      			alter database 数据库名称 charset=字符集;
      			
      			
    4. D(Delete): 删除
      	* 删除数据库
        		drop database 数据库名称;
        * 判断数据库是否存在, 存在才删除
        		drop database if exists 数据库名称;
        		
        		
    5. 使用数据库
    		* 查询当前正在使用的数据库名称
    				select database();
    		* 使用数据库(进入数据库, 可以对该数据库下的表进行操作)
    				use 数据库名称;
        
   
   
2. 操作表
		1. C(Create): 创建
			1. 语法:
				create table 表名称(
	           		列名1 数据类型1,
	              	列名2 数据类型2,
	              	......
	              	列名n 数据类型n
	            );
		            
            	* 注意: 最后一列, 不需要加逗号(,)
            	
           		* 数据类型:
            		1. int: 整数类型
            				 age int,
            		2. double: 小数类型(小数个数为5位, 保留小数点后2位)
            				 score double(5,2),  
	                3. date: 日期, 只包含年月日
	                		 yyyy-MM-dd
	                4. datetime: 日期, 包含年月日时分秒
	                		 yyyy-MM-dd HH:mm:ss
	                5. timestamp: 时间戳类型, 包含年月日时分秒
	                		 yyyy-MM-dd HH:mm:ss
	                		 * 注意: 如果将来不给这个字段赋值, 或赋值为null, 则默认使用当前的系统时间, 来自动赋值
	                6. varchar: 字符串(姓名最大20个字符)
	                		 * name varchar(20)
	                		 * 注意 zhangsan 8个字符  张三 2个字符  超出20个字符报错
                		 
                		 
	          2. 创建表:
	          		create table student(
	              		id int primary key auto_increment, (把id设置为主键 自动增长)	
	                	name varchar(32) not null, (name字符串类型, 最大长度为32, 不能为空)
	                	age int not null, (age整形, 不能为空)
	                	gender bit default 1, (gender位数据类型, (位数据类型, 取值只能为1或0), 默认值为1)
	                	score double(4,1), (score小数类型,最多小数位数为4位, 小数点后保留1位)
	                	birthday date,		(birthday日期类型, 包含年月日)
	                	insert_time timestamp (insert_time时间戳类型, 包含年月日时分秒)
	              );
              
              
	          3. 复制表: 
	          		create table 表名 like 被复制的表名;
	          		* stu表结构和student完全一样, 简称:复制表
	          				create table stu like student;
          	  4. 复制表与数据: 
	          		create table 表名 select * from 被复制的表名;
	          		* stu表结构与数据和student完全一样, 简称:复制表与数据
	          				create table stu select * from student;
          				
		2. R(Retrieve): 查询
				* 查询某个数据库中所有的表名称
						show tables;
		        * 查询某个表的创建语句:
		        		show create table 表名称;
		        * 查询表结构
		        		desc 表名称;
        		
        		
		3. U(Update): 修改
				* 修改表名
						alter table 表名称 rename to 新的表名称;
						
				* 修改表的字符集
						alter table 表名称 charset=字符集;
						
				* 添加一列
						alter table 表名称 add column 列名 数据类型;
						
				* 修改列名称和类型
						alter table 表名称 change 原列名 新列名 新数据类型;
						
		        * 修改列的类型
		        		alter table 表名称 modify 列名 新数据类型;
						* 删除列
								alter table 表名称	drop 列名称;
						
						
		4. D(Delete): 删除
				* 删除表
						drop table 表名称;
				* 判断表是否存在, 存在才删除
						drop table if exists 表名称;

DML: 增删改表中数据

1. 添加数据:
		* 语法:
				insert into 表名称(列名1, 列名2..., 列名n) values(值1, 值2....,值n);  
				* 注意:
						1. 列名和值要一一对应.
						
						2. 如果表名后, 不定义列名, 则默认给所有列添加值 (缺一个列的值都不行)
								insert into 表名称 values(值1, 值2, ..., 值n);
								
						3. 除了数字类型, 其他类型需要使用引号(单双都可以)引起来
            
            
2. 删除数据:
		* 语法:
				delete from 表名称 [可选  where 条件]
		* 注意:
				1. 如果不加条件, 则删除表中的所有记录.
				2. 如果要删除表中的所有记录.
        				1. delete from 表名称; -- 不推荐使用. 有多少条记录就会执行多少次删除操作, 效率低下
						2. truncate table 表名称;  -- 推荐使用. 先删除表, 然后在创建一个一模一样的空表, 效率高
						
						
3. 修改数据:
		* 语法:
				update 表名称 set 列名1=值1, 列名2=值2.... [可选 where 条件];
		* 注意:
				1. 如果不加任何条件, 则会将表中所有记录全部修改.

DQL: 查询表中的记录

1. 语法:
		select
				字段列表
		from
				表名列表
		where
				条件列表
		group by
				分组字段
		having
				分组之后的条件
		order by
				排序
		limit
				分页限定
				
				
2. 基础查询
		1. 多个字段的查询
				select 列1, 列2....列n from 表名称;
				* 注意:
						* 如果查询所有字段, 则可以使用*来代替字段列表.
								select * from 表名称;
								
						
		2. 去除重复查询
				* distinct -- 去除重复(结果及完全相同, 才会去除)
						select distinct 列1, 列2....列n from 表名称;
						案例:
			                -- 去除所有重复的数据
			                select distinct * from student; 
						
						
		3. 计算列
				* 一般可以使用四则运算计算一些列的值. (一般只会进行数值型的计算)
				* ifnull(表达式1, 表达式2): null参与的计算结果都为null
						* 表达式1: 那个字段需要去判断是否为null
						* 表达式2: 如果该字段为null后的替换值
								select 列1, 列2, 列3, 列2 + ifnull(列3, 0) from student; -- 列3的值为null, 把null替换为0, 列3不为null, 就用原来值进行计算
								案例:
									-- 查询 姓名 数学成绩 英语成绩 数学成绩和英语成绩之和  如果英语成绩为null把它当做0处理, 不是就用原来的值
                  					select name, math, english, math + ifnull(english, 0) from student;
								
								
		4. 起别名
				* as: as也可以省略
				
				
3. 条件查询
		1. where字句后跟条件
					select * from 表名称 where 列1 >= 条件值1 and 列2 <= 条件之2;
					案例:
			            -- 20岁 到 30岁之间有哪些人
			            select * from student where age >= 20 and age <= 30;
									
						
		2. 运算符
				* > < >= <= = != <>
						* 大于: >
						* 小于: <
						* 大于等于: >=
						* 小于等于: <=
						* 等于: =
						* 不等于: !=
						* 不等于: <>
				* between...and
						select * from 表名称 where 列 between 条件1 and 条件2;
						案例:
			                -- 20岁 到 30岁之间有哪些人
			                select * from student where age between 20 and 30;
							
							
				* in(集合)
						select * from 表名称 where 列 in (条件1, 条件2, 条件3);
						案例:
			                -- 查询年龄22岁, 18岁, 25岁的信息
			                select * from student where age in (22, 18, 25);
							
							
				* not in(集合)
						select * from 表名称 where 列 not in (条件1, 条件2, 条件3);
						案例:
			                -- 查询年龄不为22岁, 18岁, 25岁的信息
			                select * from student where age not in (22, 18, 25);
							
							
				* like: 模糊查询
						* 占位符:
								_: 任意一个字符(必须有一位)
										select * from 表名称 where 字段 like '_';
								%: 任意多个字符(0或者多个)
										select * from 表名称 where 字段 like '%';
								案例:
			                		-- 查询姓马有哪些人? like
			                    	select * from student where name like '马%';

				                    -- 查询姓名中第二个字为化的人
				                    select * from student where name like '_化%';
				
				                    -- 查询姓名是三个字的人
				                    select * from student where name like '___';
				
				                    -- 查询姓名中包含马的人
				                    select * from student where name like '%马%';
									
								
				* is null
						select * from 表名称 where 列 is null;
						案例:
			                -- 查询英语成绩为null
			                select * from student where english is null;
							
							
				* is not null
						select * from 表名称 where 列 is not null;
						案例:
				            -- 查询英语成绩不为null
				            select * from student where english is not null;
              
              
				* and 或 &&
						select * from 表名称 where 列 >= 20 and 列 <= 30;
						案例:
			                -- 20岁 到 30岁之间有哪些人
			                select * from student where age >= 20 and age <= 30;
              
              
				* or 或 ||
						select * from 表名称 where 列 = 22 or 列 = 18 or 列 = 25;
						案列:
			                -- 查询年龄22岁, 18岁, 25岁的信息
			                select * from student where age = 22 or age = 18 or age = 25;
              
              
				* not 或 !
				
				
4. 排序查询
		* 语法: order by 字句
				order by 排序字段1 排序方式1, 排序字段2 排序方式2....
		* 排序方式:
				ASC: 升序, 默认的.
				DESC: 降序
		* 注意:
				* 如果有多个排序条件, 则当前边的条件值一样时, 才会判断第二条件.
		案例:
	        -- 数学成绩升序排序
	        select * from student order by math; 
	        select * from student order by math asc; 
	
	        -- 数学成绩降序排序
	        select * from student order by math desc; 
	
	        -- 按照数学成绩升序排名, 如果数学成绩一样, 则按照英语成绩升序排名
	        select * from student order by math asc, english asc;


5. 聚合函数: 将一列数据作为一个整体, 运行纵向的计算.
		1. count: 计算个数
		        select count(字段名) from 表名称
		          
		        1. 一般选择非空的列: 主键
		            -- id为主键, 非空列
		            select count(id) from student;
		          	
		        2. count(*): 一条数据中有一个不为null, 它就会算一条数据
		            -- 一条数据中有一个不为null, 它就会算一条数据
		            select count(*) from student;
		
		2. max: 计算最大值
				select max(列) from 表名称;
				案列:
		            -- 查询数学成绩的最大值
		            select max(math) from student;
		
		
		3. min: 计算最小值
				select min(列) from 表名称;
				案列:
		            -- 查询数学成绩的最小值
		            select min(math) from student;
		
		
		4. sum: 计算和
				select sum(列) from 表名称;
				案列:
					-- 查询数学成绩的和
					select sum(math) from student;
					
		
		5. avg: 计算平均值
				select avg(列) from 表名称;
				案列:
		            -- 查询数学成绩的平均值
		            select avg(math) from student;
					
		
		* 注意: 聚合函数的计算, 排除null值.(值为null是不会计算进去)
				解决方案:
					1. 选择不包含非空的列进行计算
					2. ifnull函数
						-- 当英语成绩为null, 把它当做0, 不为0是就用原数据, 所以不存在null了
						select count(ifnull(english, 0)) from student;
						
						
6. 分组查询
		1. 语法: group by
		2. 注意:
				1. 分组之后查询的字段: 要么分组字段、聚合函数
				2. where 和 having的区别?
					1. where 在分组之前进行限定, 如果不满足条件, 则不参与分组. having在分组之后进行限定, 如果不满足结果, 则不会被查询出来
					2. where 后不可以跟聚合函数, having可以进行聚合函数的判断
		案列:
	        -- 按照性别分组, 分别查询男, 女同学的数学平均分
	        select sex, avg(math) from student group by sex;
	
	        -- 按照性别分组, 分别查询男, 女同学的数学平均分, 分别的人数
	        select sex, avg(math), count(id) from student group by sex;
	
	        -- 按照性别分组, 分别查询男, 女同学的数学平均分, 分别的人数 要求: 分数低于70分的人, 不参与分组
	        select sex, avg(math), count(id) from student where math >= 70 group by sex; 
	
	        -- 按照性别分组, 分别查询男, 女同学的数学平均分, 分别的人数 要求: 分数低于70分的人, 不参与分组, 分组之后, 人数要大于2个人
	        select sex, avg(math), count(id) from student where math >= 70 group by sex having count(id) > 2;
		

7. 分页查询
		1. 语法: limit 开始的索引, 每页查询的条数;
		2. 公式: 开始的索引 = (当前的页码 - 1) * 每页显示的条数
				案列:
					-- 每页显示3条记录
		            select * from student limit 0, 3; -- 第一页
		
		            select * from student limit 3, 3; -- 第二页
		
		            select * from student limit 6, 3; -- 第三页
    	3. limit是一个MySQL"方言"(就是limit只是mysql的操作) 

约束

* 概念: 对表中的数据进行限定, 保证数据的正确性、有效性和完整性.
* 分类:
	  1. 主键约束: primary key
	  2. 非空约束: not null
	  3. 唯一约束: unique
	  4. 外键约束: foreign key
		
		
* 非空约束: not null, 之不能为null
	1. 创建表时, 添加约束
		create table stu(
          id int, 
          name varchar(20) not null  -- name为非空
        );
    2. 创建表后, 添加非空约束
    	-- 创建表完成, 添加非空约束
        alter table stu modify name varchar(20) not null;

        -- 创建表完成, 添加非空约束
        alter table stu change name name varchar(20) not null; 
    3. 删除非空约束
    	-- 删除name的非空约束
        alter table stu change name name varchar(20); 

        -- 删除name的非空约束
        alter table stu modify name varchar(20);
        
        
* 唯一约束: unique, 之不能重复
	1. 创建表时, 添加唯一约束
        create table stu(
          id int,
          mobile varchar(11) unique -- mobile 唯一约束
        )
        * 注意mysql中, 唯一约束限定的列的值可以有多个null
        
    2. 删除唯一约束
		alter table stu drop index mobile;
				
	3. 在创建表后, 添加唯一约束
        -- 在创建表后, 添加唯一约束
        alter table stu change mobile mobile varchar(11) unique;

        -- 在创建表后, 添加唯一约束
        alter table stu modify mobile varchar(11) unique;


* 主键约束: primary key
	1. 注意:
			1. 含以: 非空且唯一
			2. 一张表只能有一个字段为主键
			3. 主键就是表中记录的唯一标识
			
	2. 创建表时, 添加主键约束
			create table stu(
	          id int primary key, -- 主键约束
	          name varchar(20)
	        )

    3. 删除主键
    		-- 错误方式: alter table stu modify id int;
    		alter table stu drop primary key;

    4. 创建完表后, 添加主键
    	-- 创建完表后, 添加主键
        alter table stu change id id int primary key;
        
        -- 创建完表后, 添加主键
        alter table stu modify id int primary key;
        
    5. 自动增长:
   		1. 概念: 如果某一列是数值类型, 使用auto_increment 可以来完成值的自动增长(一般配合数值, 主键用)
   		2. 在创建表后, 添加主键约束
           create table stu(
             id int primary key auto_increment, -- 主键约束, 自动增长
             name varchar(20)
           )

        3. 删除自动增长
        	-- 删除自动增长
            alter table stu modify id int;
            
            -- 删除自动增长
            alter table stu change id id int;

        4. 添加自动增长
        	-- 添加自动增长
            alter table stu modify id int auto_increment;
            
            -- 添加自动增长
            alter table stu change id id int auto_increment;
            
            
* 外键约束: foreign key, 让标语表产生关系, 从而保证数据的正确性
	1. 语法:
		create table 表名(
        	....
          外键列 
          constraint 外键名称 foreign key (外键列名称) references 主表名称(主表列名称)
        )
        
        create table department(  -- 部门表
        	 id int primary key auto_increment,
           dep_name varchar(20),
           dep_location varchar(20)
        );

        create table employee( -- 员工表     部门对员工:	一对多, 所以在employee表中添加外键
          id int primary key auto_increment,
          name varchar(20),
          age int,
          dep_id int, -- 外键对应主表的主键
          constraint emp_dep_id foreign key (dep_id) references department(id) -- 设置外键关系
        )
        
        
    2. 删除外键
    	 alter table 表名称 drop foreign key 外键名称;
    	 
	     -- 删除外键
	     alter table employee drop foreign key emp_dep_id;

  	
    3. 创建表之后, 添加外键
    	alter table 表名称 add constraint 外键名称 foreign key (外键列名称) references 主表名称(主表列名称);
    
	    -- 添加外键
	    alter table employee add constraint emp_dep_id foreign key (dep_id) references department(id);
      
      
    4. 级联操作
   		1. 添加级联操作
   			语法: 
   				1. 设置外键, 并添加级联更新--(把主表--外键关联字段的修改了, 从表也会自动更新)
                 	alter table 表名称 add constraint 外键名称 foreign key (外键列名称) references 主表名称(主表列名称) on update cascade;
                 
	                -- 设置外键, 并添加级联更新
	                alter table employee add constraint emp_dep_id foreign key (dep_id) references department(id) on update cascade;

             
            	2. 设置外键, 并添加级联更新, 级联删除--(把主表--外键关联字段的修改 或 删除了, 从表也会自动更新)
             		alter table 表名称 add constraint 外键名称 foreign key (外键列名称) references 主表名称(主表列名称) on update cascade on delete cascade;
             		
             		-- 设置外键, 并添加级联更新, 级联删除
                	alter table employee add constraint emp_dep_id foreign key (dep_id) references department(id) on update cascade on delete cascade;
                  
        2. 分类:
        		1. 级联更新: on update cascade
        		2. 级联删除: on delete cascade

数据库的设计

1. 多表之间的关系
	1. 分类: 
        1. 一对一: 
            * 如: 人和身份证
            * 分析: 一个人只有一个身份证, 一个身份证只能对应一个人

        2. 一对多(多对一):
            * 如: 部门和员工
            * 分析: 一个部门有多个员工, 一个员工只能对应一个部门

        3. 多对多: 
            * 如: 学生和课程
            * 分析: 一个学生可以选择很多门课程, 一个课程也可以被很多学生选择
            
    2. 实现关系:
    	1. 一对多(多对一): 
			* 如: 部门和员工
			* 实现方式: 在多的一方建立外键, 指向一的一方的主键.
						
		2. 多对多: 
            * 如: 学生和课程
            * 实现方式: 多对多关系实现需要借助第三张中间表. 中间表至少包含两个字段, 这两个字段作为第三张表的外键, 分别指向两张表的主键.
            
        3. 一对一: 
       		* 如:人和身份证
       		* 实现方式: 一对一关系实现, 可以在任意一方添加唯一外键指向另一方的主键.(建议合成一张表, 没有必要搞两张表)
        		
    3. 案例: 
    		-- 创建旅游线路分类表 tab_category
            create table tab_category (
              cid int primary key auto_increment,  -- cid 旅游线路分类主键,自动增长
              cname varchar(100) not null unique -- cname 旅游线路分类名称非空,唯一,字符串 100 
            )


            -- 创建旅游线路表 tab_route
            create table tab_route(
              rid int primary key auto_increment, -- rid 旅游线路主键,自动增长
              rname varchar(100) not null unique, -- rname 旅游线路名称非空,唯一,字符串 100
              price double not null,   -- price 价格 非空
              rdate date,  -- rdate 上架时间,日期类型 cid 外键,所属分类
              cid int, -- 外键, 指向tab_category表的主键
              -- 可以简写为foreign key (cid) references tab_category(cid) 外键名称系统会默认分配个唯一名称
              constraint cat_id foreign key (cid) references tab_category(cid) -- 创建外键关系
            )


            -- 创建用户表 tab_user
            create table tab_user (
              uid int primary key auto_increment,  -- uid 用户主键,自增长
              username varchar(100) unique not null, -- username 用户名长度 100,唯一,非空
              password varchar(30) not null, -- password 密码长度 30,非空
              name varchar(100), -- name 真实姓名长度 100
              birthday date,  -- birthday 生日
              sex char(1) default '男', -- sex 性别,定长字符串 1 默认为男
              telephone varchar(11), -- telephone 手机号,字符串 11
              email varchar(100) -- email 邮箱,字符串长度 100
            )



            -- 创建收藏表 tab_favorite 
            create table tab_favorite (
              rid int,  -- rid 旅游线路 id,外键
              date datetime, -- date 收藏时间
              uid int, -- uid 用户 id,外键
              -- 创建复合主键(联合主键), 这两个外键和起来作为这张表的主键, 他们两个的情况是不能重复的 
              primary key(rid,uid), -- rid 和 uid 不能重复,设置复合主键,同一个用户不能收藏同一个线路两次
              constraint r_fav_id foreign key (rid) references tab_route(rid), 
              constraint u_fav_id foreign key(uid) references tab_user(uid)
            )
						
2. 数据库设计的范式
		* 概念: 设计数据库时, 需要遵循的一些规范. 要遵循后边的范式要求, 必须先遵循前边的所有范式要求
			
			设计关系型数据库时,遵从不同的规范要求,设计出合理的关系型数据库。这些规范被称作范式。越高的范式数据库的冗余度就越低。
			目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴德斯科范式(BCNF)、第四范式(4NF)和第五范式(5NF 又称完美范式)
					
		* 分类:
			1. 第一范式(1NF): 每一列都是不可分割的原子数据项.(列拆分到不能拆未为止-->列不可拆分)
			2. 第二范式(2NF): 在1NF的基础上, 非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖)
					* 几个概念:
							1. 函数依赖: A --> B, 如果通过A属性(属性组)的值, 可以确定唯一B属性的值, 则称B依赖A
									例如: 学号-->姓名				(学号, 课程名称)-->分数
									
							2. 完全函数依赖: A-->B, 如果A是一个属性组, 则B属性值得确定需要依赖于A属性组中所有的属性
									例如: (学号, 课程名称) --> 分数
									
							3. 部分函数依赖: A-->B, 如果A是一个属性组, 则B属性值得确定只需要依赖于A属性组中某一些值即可
									例如: (学号, 课程名称) --> 姓名
									
							4. 传递函数依赖: A-->B, B-->C. 如果通过A属性(属性组)的值, 可以确定唯一B属性的值, 在通过B属性(属性组)的值可以确定唯一C属性的值, 则称C传递函数依赖于A
							
									例如: 学号 --> 系名, 系名 --> 系主任
									
							5. 码: 如果在一张表中, 一个属性或属性组, 被其他所有属性所完全依赖, 则称这个属性(属性组)为该表的码
									例如: 该表中码为: (学号, 课程名称)
											* 主属性: 码属性组中的所有属性
											* 非主属性: 除过码属性组的属性
											
			3. 第三范式(3NF): 在2NF基础上, 任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖) 

原数据

在这里插入图片描述

第一范式

在这里插入图片描述

第二范式

在这里插入图片描述

第三范式

在这里插入图片描述

数据库备份和还原

1. 命令行的方式
		* 语法: 
			* 备份: mysqldump -u用户名 -p密码 指定数据库的名称 > 保存的路径
			* 还原:
					1. 登入数据库: mysql -u用户名 -p密码
					2. 创建数据库: create database 数据库名称 charset=utf8
					3. 使用数据库: use 数据库名称
					4. 执行文件: source 文件路径
2. 图形化工具的方式:
		* 我这里用的navcat:
				1. 右击数据库-->转存为sql文件-->结构+数据-->删除数据库
				2. 创建数据库-->点亮(进入)数据库-->右击数据库-->运行sql文件

备份和还原演示

在这里插入图片描述

多表查询

* 查询语句:
		select 
				列表名称
		from
				表名称
		where	
				....

* 准备sql:
    -- 创建部门表
    create table dept(
      id int primary key auto_increment,
      name varchar(20)
    )

    insert into dept (name) values ('开发部'),('市场部'),('财务部'); -- 创建员工表

    create table emp (
      id int primary key auto_increment, 
      name varchar(10),
      gender char(1), -- 性别 
      salary double, -- 工资
      join_date date, -- 入职日期
      dept_id int,
      foreign key (dept_id) references dept(id) -- 外键,关联部门表(部门表的主键)
    )

    insert into emp(name,gender,salary,join_date,dept_id) values('孙悟空','男 ',7200,'2013-02-24',1);
    insert into emp(name,gender,salary,join_date,dept_id) values('猪八戒','男 ',3600,'2010-12-02',2);
    insert into emp(name,gender,salary,join_date,dept_id) values('唐僧','男',9000,'2008-08-08',2);
    insert into emp(name,gender,salary,join_date,dept_id) values('白骨精','女 ',5000,'2015-10-07',3);
    insert into emp(name,gender,salary,join_date,dept_id) values('蜘蛛精','女 ',4500,'2011-03-14',1);
    
    
* 笛卡尔积:
		* 有两个集合A, B, 去这两个集合的所有组成的情况
		* 要完成多表查询, 需要消除无用的数据
			案列:
				-- 笛卡尔积 emp, dept (emp表的数据条数 * dept表的数据条数)
				select * from emp, dept;
    
* 多表查询的分类:
		1. 内连接查询:
				1. 隐式内连接: 使用where条件消除无用数据
						案列:
							-- 查询所有员工信息和对应的部门信息
			                select * from emp, dept where emp.dept_id = dept.id;
			
			                -- 查询员工表的名称, 性别, 部门表的名称
			                select emp.name, emp.gender, dept.name from emp, dept where emp.dept_id = dept.id;
			
			                -- 取别名的方式(这是最正规的写法, 实际工作中不会把sql语句写在一行)
			                select
			                  t1.name,  -- 员工表的姓名
			                  t1.gender, -- 员工表的性别
			                  t2.name	-- 部门表的名称
			                from
			                  emp t1, -- 员工表
			                  dept t2 -- 部门表
			                where
			                  t1.dept_id = t2.id; -- 条件
                  
                  
				2. 显式内连接: 交集部分(共有的数据)
						* 语句: select 字段列表	from 表名1 inner join 表名2 on 条件
						案列: 
		                  -- 查询所有员工信息和对应的部门信息
		                  select * from emp inner join dept on emp.dept_id = dept.id;
		
		                  -- inner可以省略不写
		                  select * from emp join dept on emp.dept_id = dept.id;
             
		        3. 内连接查询:
		        		1. 从哪些表中查询数据
		        		2. 查询条件
		        		3. 查询那些字段
				
		2. 外连接查询:
				1. 左外连接: left join: 以from后的表的数据对其, from后表的数据全部显示, 不满足条件的, join后的表显示null
						* 语法: select 字段列表 from 表1 left join 表2 on 条件;
						
						* 查询的是左表所有数据以及其交集(满足条件的)部分.
		
				2. 右外连接: right join: 以join后的表的数据对其, join后表的数据全部显示, 不满足条件的, from后的表显示null
						* 语法: select 字段列表 from 表1 right join 表2 on 条件;
						
						* 查询的是右表所有数据以及其交集(满足条件的)部分.
						
		3. 子查询:
				* 概念: 查询中嵌套查询, 称嵌套查询为子查询
					  -- 查询工资最高的员工信息
		              -- 1. 查询最高的工资是多少	9000
		              select max(salary) from emp;
		
		              -- 2. 查询员工想信息, 并且工资等于9000
		              select * from emp where salary = 9000;
		
		              -- 子查询: 一条sql完成这个操作
		              select * from emp where salary = (select max(salary) from emp);
              
		        * 子查询不同情况
		        		1. 子查询的结果是单行单列的:
		            			* 子查询可以作为条件, 使用运算符去判断. 运算符: > >= < <= =...单值判断的
		            			案列:
		            				-- 查询员工工资小于平均工资的人
									select * from emp where salary < (select avg(salary) from emp);
													
		        		2. 子查询的结果是多行多列的:
		        					* 子查询可以作为条件, 使用运算符in来判断	
		        						-- 子查询: 查询"财务部"和"市场部"所有的员工信息
		                      			select * from emp where emp.dept_id in (select dept.id from dept where name = "财务部" or name = "市场部");
		        		
		        		3. 子查询的结果是多行多列的:
		        					* 子查询可以作为一张虚拟表参与查询
		        							-- 子查询: 查询员工的入职日期是2011-11-11之后的员工信息和部门信息
											select * from dept t1, (select * from emp where join_date > "2011-11-11") t2 where t1.id = t2.dept_id;
													
											-- 普通内连接
											select * from dept, emp where dept.id = emp.dept_id and emp.join_date > "2011-11-11";
											
	  4. 多表查询案例:
	  	  -- 部门表
          CREATE TABLE dept (
            id INT PRIMARY KEY, -- 部门id
            dname VARCHAR(50), -- 部门名称
            loc VARCHAR(50) -- 部门所在地
          );

          -- 添加4个部门
          INSERT INTO dept(id,dname,loc) VALUES 
          (10,'教研部','北京'),
          (20,'学工部','上海'),
          (30,'销售部','广州'),
          (40,'财务部','深圳');



          -- 职务表,职务名称,职务描述
          CREATE TABLE job (
            id INT PRIMARY KEY,
            jname VARCHAR(20),
            description VARCHAR(50)
          );

          -- 添加4个职务
          INSERT INTO job (id, jname, description) VALUES
          (1, '董事长', '管理整个公司,接单'),
          (2, '经理', '管理部门员工'),
          (3, '销售员', '向客人推销产品'),
          (4, '文员', '使用办公软件');



          -- 员工表
          CREATE TABLE emp (
            id INT PRIMARY KEY, -- 员工id
            ename VARCHAR(50), -- 员工姓名
            job_id INT, -- 职务id
            mgr INT , -- 上级领导
            joindate DATE, -- 入职日期
            salary DECIMAL(7,2), -- 工资
            bonus DECIMAL(7,2), -- 奖金
            dept_id INT, -- 所在部门编号
            CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job (id),
            CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept (id)
          );

          -- 添加员工
          INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES 
          (1001,'孙悟空',4,1004,'2000-12-17','8000.00',NULL,20),
          (1002,'卢俊义',3,1006,'2001-02-20','16000.00','3000.00',30),
          (1003,'林冲',3,1006,'2001-02-22','12500.00','5000.00',30),
          (1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
          (1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
          (1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
          (1007,'刘备',2,1009,'2001-09-01','24500.00',NULL,10),
          (1008,'猪八戒',4,1004,'2007-04-19','30000.00',NULL,20),
          (1009,'罗贯中',1,NULL,'2001-11-17','50000.00',NULL,10),
          (1010,'吴用',3,1006,'2001-09-08','15000.00','0.00',30),
          (1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
          (1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
          (1013,'小白龙',4,1004,'2001-12-03','30000.00',NULL,20),
          (1014,'关羽',4,1007,'2002-01-23','13000.00',NULL,10);



          -- 工资等级表
          CREATE TABLE salarygrade (
            grade INT PRIMARY KEY,   -- 级别
            losalary INT,  -- 最低工资
            hisalary INT -- 最高工资
          );

          -- 添加5个工资等级
          INSERT INTO salarygrade(grade,losalary,hisalary) VALUES 
          (1,7000,12000),
          (2,12010,14000),
          (3,14010,20000),
          (4,20010,30000),
          (5,30010,99990);

          -- 需求:
          -- 1.查询所有员工信息。查询员工编号,员工姓名,工资,职务名称,职务描述
          /*
            分析:
              1. 员工编号,员工姓名,工资,需要查询emp表 职务名称,职务描述, 需要查询job表
              2. 查询条件: emp.job_id = job.id
          */
          select 
            emp.id,  -- 员工编号
            emp.ename,  -- 员工姓名
            emp.salary,  -- 员工工资
            job.jname,  -- 职务名称
            job.description -- 职务描述
          from 
            emp, -- 员工表
            job  -- 职务表
          where 
            emp.job_id = job.id;


          -- 2.查询员工编号,员工姓名,工资,职务名称,职务描述,部门名称,部门位置
          /*
            分析:
              1. 员工编号,员工姓名,工资,需要查询emp表   职务名称,职务描述, 需要查询job表    部门名称,部门位置, 需要查询dept表
              2. 查询条件: emp.job_id = job.id and emp.dept_id = dept.id
          */
          select
            emp.id,   -- 员工编号
            emp.ename,  -- 员工姓名
            emp.salary,  -- 员工工资
            job.jname,  -- 职务名称
            job.description,  -- 职务描述
            dept.dname,  -- 部门名称
            dept.loc  -- 部门位置
          from
            emp,  -- 员工表 
            job,  -- 职务表
            dept  -- 部门表
          where
            emp.job_id = job.id 
            and emp.dept_id = dept.id;


          -- 3.查询员工姓名,工资,工资等级
          /*
            分析:
              1. 员工姓名,工资, 需要查询emp表   工资等级, 需要查询salarygrade表
              2. 查询条件: emp.salary >= salarygrade.losalary and emp.salary <= salarygrade.hisalary;
                          或
                    emp.salary between salarygrade and salarygrade.hisalary;
          */
          select
            emp.ename, -- 员工姓名
            emp.salary,  -- 员工工资
            salarygrade.grade  -- 工资等级
          from 
            emp,  -- 员工表
            salarygrade  -- 工资等级表
          where 
            emp.salary >= salarygrade.losalary 
            and emp.salary <= salarygrade.hisalary;


          select
            emp.ename, -- 员工姓名
            emp.salary,  -- 员工工资
            salarygrade.grade  -- 工资等级
          from 
            emp,  -- 员工表
            salarygrade  -- 工资等级表
          where 
            emp.salary between salarygrade.losalary and salarygrade.hisalary;


          -- 4.查询员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级
          /*
            分析:
              1. 员工姓名,工资, 需要查询emp表   职务名称,职务描述, 需要查询job表    部门名称,部门位置,需要查询dept表   工资等级, 需要查询salarygrade表
              2. 查询条件: emp.job_id = job.id and emp.dept_id = dept.id and emp.salary >= salarygrade.losalary and emp.salary <= salarygrade.hisalary;
                                  或
                    emp.job_id = job.id and emp.dept_id = dept.id and emp.salary between salarygrade.losalary and salarygrade.hisalary;
          */
          select
            emp.ename,  -- 员工姓名
            emp.salary,  -- 员工工资
            job.jname,  -- 职务名称
            job.description,  -- 职务描述
            dept.dname,  -- 部门名称
            dept.loc,  -- 部门位置
            salarygrade.grade  -- 工资等级
          from
            emp,  -- 员工表
            job,  -- 职务表
            dept,  -- 部门表	
            salarygrade  -- 工资等级表
          where
            emp.job_id = job.id 
            and emp.dept_id = dept.id 
            and emp.salary >= salarygrade.losalary 
            and emp.salary <= salarygrade.hisalary;


          select
            emp.ename,  -- 员工姓名
            emp.salary,  -- 员工工资
            job.jname,  -- 职务名称
            job.description,  -- 职务描述
            dept.dname,  -- 部门名称
            dept.loc,  -- 部门位置
            salarygrade.grade  -- 工资等级
          from
            emp,  -- 员工表
            job,  -- 职务表
            dept,  -- 部门表	
            salarygrade  -- 工资等级表
          where
            emp.job_id = job.id 
            and emp.dept_id = dept.id 
            and emp.salary between salarygrade.losalary and salarygrade.hisalary;


          -- 5.查询出部门编号、部门名称、部门位置、部门人数
          /*
            分析:
              1. 部门编号、部门名称、部门位置、需要查询dept表   部门人数 需要查询emp表
              2. 使用分组查询, 按照emp.dept_id完成分组, 查询count(id)
              3. 使用子查询将第2步的查询结果和dept表进行关联查询
          */
          -- select
          -- 	dept_id,
          -- 	count(id)
          -- from
          -- 	emp
          -- group by
          -- 	dept_id;

          select 
            t1.id,  -- 部门编号
            t1.dname,  -- 部门名称
            t1.loc,  -- 部门位置
            t2.total  -- 部门人数
          from 
            dept t1,  -- 部门表
            (select
              dept_id,  -- 所在部门编号
              count(id) as total  -- 每个部门的总人数
            from
              emp
            group by
              dept_id) t2  -- 把根据dept_id分组的结果作为一张虚拟表
          where 
            t1.id = t2.dept_id;


          -- 6.查询所有员工的姓名及其直接上级的姓名,没有领导的员工也需要查询
          /*
            分析:
              1. 姓名 emp   直接上级的姓名 emp
                    * emp表的id 和 mgr 是自关联
              2. 条件 emp.id = emp.mgr
              3. 查询做表的所有数据, 和 交集数据
                    * 舒勇左外连接查询
          */

          /*
            select 
              t1.ename,
              t1.mgr,
              t2.id,
              t2.ename   这个只查询出来了有领导的
            from
              emp t1,
              emp t2
            where
              t1.mgr = t2.id
          */

          select 
            t1.ename,  -- 员工名称
            t1.mgr,	   -- 管理者id
            t2.id,	   -- 管理者id
            t2.ename   -- 管理者名称
          from	
            emp t1
          left join
            emp t2
          on
            t1.mgr = t2.id;

事务

1. 事务的基本介绍
	1. 概念:
			* 如果一个包含多个步骤的业务操作, 被事务管理, 那么这些操作要么同时成功, 要么同时失败.
				
	2. 操作:
			1. 开启事务: start transaction;
			2. 回滚: rollback;
			3. 提交: commit;
				
	3. 案例:
		    -- 创建数据表
            CREATE TABLE account (
               id INT PRIMARY KEY AUTO_INCREMENT,
               NAME VARCHAR(10),
               balance DOUBLE
            );
            -- 添加数据
            INSERT INTO account (NAME, balance) VALUES ('zhangsan', 1000), ('lisi', 1000);



	        -- 张三转500给李四
	        /*
	            分析:
	                  开启事务
	              1. 查询张三账户余额是否大于500
	              2. 张三账户金额 - 500    出现异常 回滚
	              3. 李四账户金额 + 500
	                  提交事务
	        */
	
	        -- 0. 开启事务
	        start transaction;
	        
	        -- 1. 查询张三账户余额是否大于500
	        select * from account where name = "zhangsan" and balance > 500;
	
	        -- 2. 张三账户金额 - 500
	        update account set balance = balance - 500 where name = "zhangsan";
	
	        -- 出错了....
	
	        -- 3. 李四账户金额 + 500
	        update account set balance = balance + 500 where name = "lisi";
	
			-- rollback commit  二者选其一  执行成功了就commit提交事务   执行失败了就回滚到开启事务那
					
	        -- 发现出问题了--回滚事务
	        rollback;
	
	        -- 成功了--提交事务 
	        commit;
          
   4. MySQL数据中事务默认自动提交    			
    			* 事务提交的两种方式:
    					* 自动提交:
    							* mysql就是自动比较的
    							* 一条DML(增删改)语句会自动提交一次事务.
    					* 手动提交:
    							* Oracle数据库默认是手动提交事务
    							* 需要先开启事务, 在提交
    							
    			* 修改事务的默认提交方式:
    					* 查看事务的默认提交方式: select @@autocommit; -- 1 代表自动提交  0 代表手动提交
    					* 修改默认提交方式: set @@autocommit = 0; -- 修改后不管是什么语句执行了, 你都需要commit来提交事务
    					
2. 事务的四大特征:
		1. 原子性: 是不可分割的最小操作单位, 要么同时成功, 要么同时失败
		2. 持久性: 当事务提交或回滚后, 数据库会持久化的保存数据
		3. 隔离性: 多个事务之间, 相互独立.
		4. 一致性: 事务操作前后, 数据总量不变
		
		
3. 事务的隔离级别
		* 概念: 多个事务之间隔离, 相互独立的. 但是如果多个事务操作同一批数据, 则会发生一些问题, 设置不同的隔离级别就可以解决这些问题.
		
		* 存在问题:
				1. 脏读: 一个事务, 读取到另一个事务中没有提交的数据.
				2. 不可重复度(虚读): 在同一个事务中, 两次读取到的数据不一样.
				3. 幻读: 一个事务操作(DML)数据表中所有记录, 另一个事务添加了一条数据, 则第一个事务查询不到自己的修改.
				
		* 隔离级别:
				1. read uncommitted: 读未提交
						* 产生的问题: 脏读、不可重复读、幻读
				
				2. read committed: 读已提交 (Oracle默认)
						* 产生的问题: 不可重复读、幻读
						
				3. repeatable read: 可重复读 (MySQL默认)
						* 产生的问题: 幻读
						
				4. serializable: 串行化-->(意思就是锁表, 跟多线程的锁一样, 只有一个事务被提交或回滚了, 才能对这张表继续操作, 在没提交或回滚时, 对着张表的操作都是等待这的, 不会执行)
						* 可以结果所有的问题
						
				* 注意: 隔离级别从小到大安全性越来越高, 但是效率越来越低.
				
				* 数据库查询隔离级别:
						1. 在"5.7.20"版本之前
									select @@tx_isolation;
						2. 在"5.7.20"版本之后									
									select @@transaction_isolation;
									
				* 数据库设置隔离级别:
						set global transaction isolation level 级别字符串;
						案列:
								-- 设置成-->串行化
								set global transaction isolation level serializable;
								
								
		* 演示:
				0. 开两个终端
				
				1. 第一个终端-->设置隔离级别为-->读未提交: set global transaction isolation level read uncommitted;
				
				2. 第二个终端连接数据库-->(设置了隔离级别, 第二个终端才能连接数据库, 提前连接, 不起作用, 因为隔离级别设置后, 第二个终端要是之前连接这的就不会生效, 只有重新连接才会生效, 设置隔离级别的那个终端立即生效)
				
				3. 两个终端同时-->开启事务: start transaction;
				
				4. 第一个终端-->转账:
        			update account set balance = balance - 500 where name = "zhangsan";
        			update account set balance = balance + 500 where name = "lisi";
        			没提交(commit), 另一个终端也是可以读取到修改后的数据 
        			
        			.....
        			
        			其他的隔里级别, 步足同上
        					read uncommitted: 
        								会出现(第一个终端)未提交的数据, (第二个终端)可以读取到-->(脏读)
        								当(第一个终端commit或rollback)时, (第二个终端)查询的数据发生改变-->(不可重复读)
        								
        					read committed:
        								(第一个终端没commit或rollback)数据, (第二个终端)读取的始终是原来的数据
        								当(第一个终端commit或rollback)提交数据了, (第二个终端)读取的就是提交后的数据-->(不可重复读)
        								
        					repeatable read:
        								(第一个终端没commit或rollback)数据, (第二个终端)读取的始终是原来的数据
        								当(第一个终端commit或rollback)提交数据了, (第二个终端)读取的还是原来的数据
        								只有当(第二个终端commit或rollback)提交数据了, 才能读取到新的数据
        								
        					serializable:
        								当(第一个终端没commit或rollback)数据, (第二个终端)使用无法对该表进行操作,
        								只有当(第一个终端commit或rollback)数据, (第二个终端)才能对该表进行操作
        			
        			* 注意: 幻读, 在mysql上是演示不了的
        			

DCL

* SQL分类:
		1. DDL: 操作数据库和表
		2. DML: 增删改表中的数据
		3. DQL: 查询表中的数据
		4. DCL: 管理用户, 并对用户进行授权
		
* DBA: 数据库管理员

* DCL: 管理用户, 并对用户进行授权
		1. 管理用户
				1. 添加用户:--->(主机名, 在mysql数据库下的user表里有一个host字段, 这个字段的值就是主机名)
						* 语法:
								create user '用户名'@'主机名' identified by '密码';
								案列:
									-- 只能在本机上使用用户登录数据库
									create user 'zhangsan'@'localhost' identified by '123456';
									
									-- 可以在任意主机使用用户登录数据库
                   					create user 'lisi'@'%' identified by '123456';
				2. 删除用户:
						 * 语法:
						 		-- 删除用户
                				drop user '用户名'@'主机名';
								案列:
                					drop user 'zhangsan'@'localhost';
                		
				3. 修改用户密码:
						 * 语法:
								1. 老版本的改密码方式:
		                			update user set password = password("新密码") where user = "用户名";
		                			案列:
	                					-- lisi的密码修改为abc
	                					update user set password = password("abc") where user = "lisi";
								
									set password for "用户名"@"主机名" = password("新密码");
										案列:
											-- lisi的密码修改为abc
											set password for "lisi"@"%" = password("abc");
								
								2. 新版本修改密码的方式:
		                			ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码';
			                			案列:
											-- lisi的密码修改为abc
											ALTER USER 'lisi'@'%' IDENTIFIED WITH mysql_native_password BY 'abc';
													
									* mysql中忘记了root用户密码?
											0. 打开终端
											
											1. 关闭mysql服务器
													 * linux: service mysql stop
													 * mac: mysql.server stop
													 * window: net stop mysql
													 
											2. 使用无验证方式启动mysql服务: 
														mysqld --skip-grant-tables
														
											3. 在开一个终端直接连接mysql服务: 
														mysql  (直接输入mysql, 不需要用户名和密码)
														
											4. 切换到mysql表:
														use mysql
														
											5. 修改root用户密码:
														1. 老版本的改密码方式:
							                                update user set password = password("新密码") where user = "用户名";
							                                案列:
							                                    -- lisi的密码修改为abc
							                                    update user set password = password("abc") where user = "root";
							
							                                set password for "用户名"@"主机名" = password("新密码");
							                                案列:
							                                    set password for "root"@"localhost" = password("abc");
	
								                        2. 新版本修改密码的方式:
								                          	* 注意新版本在mysqld --skip-grant-tables服务下进入的mysql, 在修改root用户前需要先执行:
								                          		flush privileges;
								                          					
								                            ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码';
								                            案列:
							                                    -- lisi的密码修改为abc
							                                    ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'abc';
	                                    
						                    6. 查看mysqld进程, 杀死进程
						                     	1. mac:
						                              ps -a | grep mysqld
						
						                              kill -9 mysqld的PID
						                              
						                          2. linux:
						                          		ps -aux | grep mysqld
						                          		
						                          		kill -9 mysqld的PID
						                          		
						                          3. window:
						                          		1. 打开任务管理器
						                          		2. 找到mysqld进程, 结束进程
						                     			
						                     7. 开启mysql服务:
						                     			1. mac:
						                     		  		mysql.server start
						                     		  		
						                     		  2. linux:
						                     		  		service mysql start
						                     		  		
						                     		  3. window:
						                     		  		net start mysql
						                     		  
						                      8. 用新密码登入mysql
						                     			mysql -uroot -pabc;
				

				4. 查询用户:
						-- 1. 切换到mysql数据库
			            use mysql;
			
			            -- 2. 查询user表
			            select * from user;
			            
			            * 通配符: % 表示可以在任意主机使用用户登录数据库
		
		
		
		2. 权限管理:
				1. 查询权限:
						-- 查询权限
			            show grants for "用户名"@"主机名";
			
						-- 查询lisi权限
			            show grants for "lisi"@"%";
			
						-- 查询root权限
			            show grants for "root"@"localhost";
            
				2. 授予权限:
						-- 授予权限
						grant 权限列表 on 数据库.表名 to "用户名"@"主机名";
						案列:
							-- 给lisi授予查询的权限
							grant select on test3.account to "lisi"@"%";
							
							-- 给lisi授予查询, 删除的权限
							grant select, delete on test3.account to "lisi"@"%";
							
							-- 给张三用户授予所有权限, 在任意数据库任意表上-->(all代表所有权限 *.*代表所有数据库下的所有表)
							grant all on *.* to "zhangsan"@"localhost";
				
				3. 撤销权限:
						-- 撤销权限:
						revoke 权限列表 on 数据库.表名 from "用户名"@"主机名";
						案列:
							-- 撤销lisi的删除权限
							revoke delete on test3.account from "lisi"@"%";
					
							-- 撤销lisi的查询和删除权限
							revoke select, delete on test3.account from "lisi"@"%";
							
							-- 撤销张三的所有权限-->(all代表所有权限 *.*代表所有数据库下的所有表)
							revoke all on *.* from "zhangsan"@"localhost";
  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只因为你温柔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值