admin管理员组

文章数量:1647017

mysQl

一范式:列不可分()

二范式:在一范式的基础上(一张表只描述一个内容)

三范式:在二范式的基础上(要有主键)

各种函数的使用
–基本函数:lower(参数变小写)\upper(变大写)\length\substr\concat\replace\ifnull\uuid
–小数的函数:round\ceil \floor

#对小数的处理
#round(四舍五入) & ceil(向上取整) & floor(全舍)
select comm,ceil(comm),floor(comm),round(comm) from emp;

–日期的函数:now\year\ month \ day\hour\minute \second\转义字符

–条件函数distinct/where/like/order by/limit/

修改   
update 表1 别名
inner|left|right join 表2 别名
on 连接条件
set 列=,=值
wehre 筛选条件

删除
DELETE t1,t2 FROM A t1 LEFT JOIN B t2 ON t1.Id = t2.id where t1.Id = 2
delete from product where id
插入
INSERT INTO  [表名]([列名],[列名])  VALUES ([列值],[列值])), ([列值],[列值])), ([列值],[列值]));

--40.查询选修“赵”老师所授课程的学生中,成绩最高的学生姓名及其成绩  
select b.stuNo,a.stuName,max(score) from student a,score b  
where a.stuNo=b.stuNo and b.cNo in 
(select a.cNo from course a,teacher b where a.tNo=b.tNo and b.tName='钱市保')  
group by b.stuNo,a.stuName,b.cNo  
having b.cNo in 
(select a.cNo from course a,teacher b where a.tNo=b.tNo and b.tName='钱市保')  

substring(字段名,7(从那开始(数据库是从1开始算的)),44位))
-
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (//创建表
  `id` bigint(0) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `score` int(0) NOT NULL,
  `banji` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE //主键
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

列转行
SELECT year,
sum(if(month=1,amount,0)) m1,
sum(if(month=2,amount,0)) m2,
sum(if(month=3,amount,0)) m3,
sum(if(month=4,amount,0)) m4
from table2 GROUP BY year

select year,
sum(case when month = 1 then amount else NULL end) m1,
sum(case when month = 2 then amount else NULL end) m2,
sum(case when month = 3 then amount else NULL end) m3,
sum(case when month = 4 then amount else NULL end) m4
from table2 GROUP BY year

创建索引

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iOEebJqj-1666622177696)(D:\桌面\bug集\扩展\typora-img\image-20220929165836528.png)]

创建唯一索引:创建唯一索引的列的值不能重复
– create unique index <index_name> on表名(列名)
create unique index index_testl on tb_testindex(tid):

创建普通索引:不要求创建索引的列的值的唯一性

–create index on表名(列名);
create index index-test2 on tb_testindex(name);

创建组合索引

–create index on 表名(列名1,列名2…)
create index index_test3 on tb_testindex(tid,name);

#删除索引
DROP INDEX <索引名> ON <表名>
#添加唯一索引
desc teachers;
alter table teachers add UNIQUE(prof);#检查索引列的值必须唯一
#添加复合索引
alter table teachers add index many_index(tno,tname)

#查看索引
show index <索引名> 表名 from 库名

  • Table 表示创建索引的数据表名,这里是 tb_stu_info2 数据表。
  • Non_unique 表示该索引是否是唯一索引。若不是唯一索引,则该列的值为 1;若是唯一索引,则该列的值为 0。
  • Key_name 表示索引的名称。
  • Seq_in_index 表示该列在索引中的位置,如果索引是单列的,则该列的值为1;如果索引是组合索引,则该列的值为每列在索引定义中的顺序。
  • Column_name 表示定义索引的列字段。
  • Collation 表示列以何种顺序存储在索引中。在 MySQL 中,升序显示值“A”(升序),若显示为 NULL,则表示无分类。
  • Cardinality 索引中唯一值数目的估计值。基数根据被存储为整数的统计数据计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机会就越大。
  • Sub_part 表示列中被编入索引的字符的数量。若列只是部分被编入索引,则该列的值为被编入索引的字符的数目;若整列被编入索引,则该列的值为NULL。
  • Packed 指示关键字如何被压缩。若没有被压缩,值为 NULL。
  • Null 用于显示索引列中是否包含 NULL。若列含有 NULL,该列的值为 YES。若没有,则该列的值为 NO。
  • Index_type 显示索引使用的类型和方法(BTREE、FULLTEXT、HASH、RTREE)。
  • Comment 显示评注。

视图的创建

create view 视图名 as 条件(select * from 表)

触发器

SELECT * FROM information_schema.**triggers**; //触发器存放在**triggers**中
DROP TRIGGER 触发器名称	//删除触发器

delimiter //
CREATE TRIGGER [触发器的名字]
[触发器执行时机] [触发器监测的对象]
ON [表名]
FOR EACH ROW [触发器主体代码]//
DELIMITER ;
-----------------------------------------------
###### BEFORE  先看触发条件符不符合符合执行,不符合报错
###### AFTER  先执行语句在触发
----------------------------------------------------
DELIMITER //
CREATE TRIGGER customer_status_records
AFTER INSERT 
ON customers
FOR EACH ROW
Insert into customer_status(customer_id, status_notes) VALUES(NEW.customer_id, '账户创建成功')//
DELIMITER ;
-----------------------------------------------------
DELIMITER //
CREATE TRIGGER log_sales_updates
AFTER UPDATE
ON sales
FOR EACH ROW
Insert into audit_log(sales_id, previous_amount, new_amount, updated_by, updated_on) VALUES (NEW.sales_id,OLD.sales_amount, NEW.sales_amount,(SELECT USER()), NOW() )//
DELIMITER ;
-------------------------------------------------------
DELIMITER //
CREATE TRIGGER delete_related_info
AFTER DELETE
ON sales
FOR EACH ROW
Delete from customers where customer_id=OLD.customer_id;//
DELIMITER ;

触发器的结构包括:

  • DELIMITER //:MySQL 默认分隔符是; 但在触发器中,我们使用 // 表示触发器的开始与结束。
  • [触发器的名字]:这里填写触发器的名字
  • [触发器执行时机]:这里设置触发器是在关键动作执行之前触发,还是执行之后触发。
  • [触发器监测的对象]:触发器可以监测 INSERTUPDATEDELETE 的操作,当监测的命令对触发器关联的表进行操作时,触发器就被激活了。
  • [表名]:将这个触发器与数据库中的表进行关联,触发器定义在表上,也附着在表上,如果这个表被删除了,那么这个触发器也随之被删除。
  • FOR EACH ROW:这句表示只要满足触发器触发条件,触发器都会被执行,也就是说带上这个参数后,触发器将监测每一行对关联表操作的代码,一旦符合条件,触发器就会被触发。
  • [触发器主体代码]:这里是当满足触发条件后,被触发执行的代码主体。这里可以是一句 SQL 语句,也可以是多行命令。如果是多行命令,那么这些命令要写在 BEGIN...END 之间。

**注:**在创建触发器主体时,还可以使用OLDNEW 来获取 SQL 执行INSERTUPDATEDELETE 操作前后的写入数据。这里没看明白没关系,我们将会在接下来的实践中,展开讲解。

存储过程

delimiter $$ --自定义结束符
create procedure p2()
begin
	SELECT id,username from `user`;
end $$
delimiter;
--调用存储过程
call P2();
----------传入参数:in----------------
delimiter $$ --自定义结束符
create procedure p3(in param_id int)
begin
	SELECT *from `user` where `user`.id = param_id;
end $$
delimiter;

call p3(2);
------------存储过程   if-----------------
create procedure p4()
begin
	declare count int default 40;--定义变量
	declare result varchar(10);
	if count >=50 then
		set result :='优秀';
	else if count >=40 then
		set result :='良好';
	else
		set result :='不及格';
	end if;
	SELECT result;
end;

call p4()
------------存储过程   in输入out输出inout即输入又输出-----------------
create procedure p5(in count int,out result varchar(10))
begin
	if count >=50 then
		set result :='优秀';
	elseif count >=40 then
		set result :='良好';
	else
		set result :='不及格';
	end if;
	SELECT @result;
end;

call p5(45,@result);

事务

ACID实现原理

A 是原子性是指多条SQL要么都成功要么都失败
C 是一致性数据不管怎么操作都是一致的
I 是隔离性数据库允许高并发,同时增删改查数据
D 是持久性是指对数据的操作持久有效
  1. 脏读:就是事务A读出了一条不存在的数据
  2. 不可重复读:数据读的不一致(事务 A 多次读取同一数据,但事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
  3. 幻读:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。

隔离级别

串行话效率最低,安全性最高(实现表级锁)
读未提交效率最高,但是安全性最差(多并发时数据可能出错)
读已提交(RC效率较低,但是安全性较高(oracle数据库的默认级别)
可重复度(RRmysql默认级别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yQnJN4py-1666622177697)(D:\桌面\bug集\扩展\typora-img\image-20220930104649063.png)]

悲观锁在处理数据的时候会认为别的线程也会同步修改数据,所有会上锁,这样别的线程想拿到数据就会阻塞直到它拿到锁
实现:synchronized lock 先加锁的都是悲观锁(适合写操作)
乐观锁就是多个线程处理数据时认为别的线程不会同时修改数据,所有不会上锁,但是在更新的时候会判断在此之间别的线程有没有更新过这个数据
实现:CAS实现、Version版本号控制(适合读操作)
全局锁对整个数据库实例加锁,加锁后整个实例就处于只读状态后面的DDL、DML就会被阻塞。 典型的使用场景就是全库的逻辑备份,对所有的表进行锁定从而获取一致性试图,保证数据的完整性flush tables whit read lock;//加全局锁 unlock tables;//释放锁
行锁锁定单个行记录的锁,防止其他事物对此进行update和delect。在RC、RR隔离级别凑支持
表锁1.读锁 不会阻塞其他客户端的读会阻塞写
2.写锁 既阻塞其他客户端的读又阻塞写
语法:
1.加锁:lock tables 表名…read/write
2.释放锁:unlock tables 客户端断开连接
意向锁1.意向共享锁(IS):语句 select …lock in share mode添加。
与表锁共享锁read兼容,于表锁排它锁write互斥。
2.意向排他锁(IX):由insert、update、delete、select…for update添加。
与表锁共享锁read级排它锁write都互斥,意向锁之间不会互斥。
主要解决表锁和行锁冲突的问题行级锁
每次操作锁住对应的行数据.锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在innoDB存储引擎中.。
元数据锁为了避免DML于DDL冲突,保证读写的正确性
意向锁1.意向共享锁(IS):语句 select …lock in share mode添加。
与表锁共享锁read兼容,于表锁排它锁write互斥。
2.意向排他锁(IX):由insert、update、delete、select…for update添加。
与表锁共享锁read级排它锁write都互斥,意向锁之间不会互斥。
主要解决表锁和行锁冲突的问题
间隙锁锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事物在这个间隙进行insert,产生幻读。在RR隔离级别下支持。
临键锁临键锁(Nest-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。

InnoDB实现了以下俩种类型的行锁:
1.共享锁(s):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
2.排他锁(x):允许获取排他锁的事务更新数据,防止其他事务获得相同数据集的共享锁和排他锁。

间隙锁/临键锁
1.索引上的等值查询(唯一索引),给不存在的记录加锁时优化为间隙锁。
2.索引上的等值查询(普通索引),向有遍历时最后一个值不满足查询需求时,nest-key losk退化为间隙锁。

存储过程

InnoDB
是一种兼顾高可靠性和高性能的通用存储引擎,在Mysql5.5后,成为Mysql存储引擎
特点
支持事务
行级锁,提高并发访问性能
支持外键FOREIGN KEY约束,保证数据的完整性和正确性
逻辑存储结构
TablsSpece:表空间
Segment:段
Page:页
Row:行
文件
xxx.ibd:xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表jieg(frm、sdi)、数据和索引。
参数:innodb_file_per_table
MylSAM
早期默认存储引擎
特点
不支持事务和外键
支持表锁,不支持行锁
访问速度快
文件
xxx.sdi:存储表结构信息
xxx.MYD:存储数据
xxx.MYI:存储索引
Memory
他的表数据是存储在内存中的,由于受到硬件问题、或者断电问题的影响,只能作为临时表或缓存使用。
特点
内存存放(查询快)
hash索引(默认)
文件
xxx.sdi:存储表结构信息


一,数据库
索引分类 主键 单值 复合 唯一
主键 Primary Key
外键 Foreign Key(子表字段)references(主表字段)
复合 INDEX idx_phone_cardno
唯一 Unique
非空 not null
默认 default
检查 check
自增 AUTO_INCREMENT
l 第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列。

如电话列可进行拆分—家庭电话、公司电话

l 第二范式(2NF):首先是 1NF,另外包含两部分内容,一是表必须有主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。

l 第三范式(3NF):首先是 2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。

--1,概述
	存储和管理数据的一个仓库,分为两种数据库:关系型数据库和非关系型数据库
	关系型数据库:表于表之间有联系
	非关系型数据库:数据间的关系不紧密-NoSQL
	关系型数据库产品:MySQL:免费的 / Oracle:收费的
	英文是database/DB
--2,MySQL数据库
	--安装mysql服务器端:
		-设置端口号(默认是3306) + 密码(root) + 编码(默认是latin1->utf-8)
	--如何检查数据库是否安装成功?
		--执行DOS命令:
			-找到开始位置的MySQL-MySQL Command line Client-输入密码-展示Welcome就可以了
			-win+r-输入cmd-输入命令:
				C:\Users\Administrator>mysql -uroot -proot
				-展示Welcome就可以了
		--用客户端工具连接:
			点击连接-输入连接名-输入密码(检查端口号)-连接测试-连接成功-确定
--3,数据库的结构
	--数据库--表--行(一条一条的记录)和列(字段)

unsigned	表示不能为负数	
CREATE TABLE `pms_album` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '记录id',
  `name` varchar(50) DEFAULT NULL COMMENT '相册名称',
  `description` varchar(255) DEFAULT NULL COMMENT '相册简介',
  `sort` tinyint unsigned DEFAULT '0' COMMENT '自定义排序序号',
  `gmt_create` datetime DEFAULT NULL COMMENT '数据创建时间',
  `gmt_modified` datetime DEFAULT NULL COMMENT '数据最后修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='相册';	

二,SQL语言
–1,概述
用来操作数据库的语言。可以对数据库里的数据进行CRUD增删改查
分类: DML-数据操纵语言(CRUD)
DDL-数据库定义语言(创建库、创建表)
DCL-数据库控制语言(分配具体权限)
DQL-数据库的查询语言(各种查的语法)
–2,用法:CRUD
–数据库:查询、创建、删除库(谨慎)
–表:查询、创建、删除、修改
–!!!记录:创建、查询、删除、修改
–3,操作数据库的SQL
–查询所有数据库:show databases;
–新建数据库:create database 数据库名;
–新建数据库:create database 数据库名 default character set utf8;
–删除数据库:drop database 数据库名;
–4,操作数据表的SQL
–使用指定数据库:use 数据库名;
–查询所有数据表:show tables;
–新建数据表:
字段类型:int/long double/float char/varchar
create table 表名(
字段1名字 字段类型(字段长度),
字段2名字 字段类型(字段长度),
字段3名字 字段类型(字段长度)
)

		 	mysql> create table tb_order_detail(
			    -> id int(11),
			    -> order_id int(11),
			    -> num int(4),
			    -> item varchar(30)
			    -> );
	--查看数据表结构:desc 表名
		mysql> desc tb_door;
	--修改数据表:alter table 表名 add column 字段名 字段类型(字段长度)
		mysql> alter table tb_door add column addr varchar(100);
		mysql> desc tb_door;
	--删除数据表:drop table 表名;
		mysql> drop table tb_order_detail;
--5,!!!!操作记录的SQL
                            --  插入记录    
                                   insert into tb_door values(null,’永和大王1店’,666);
                                   insert into tb_door values(null,’ 永和大王2店’,888);
	--查询记录:select 字段名 / * from 表名
		mysql> select * from student;
	--添加记录:insert into 表名 values(字段1的值,字段2的值,字段3的值);
		mysql> set names gbk; #防止中文乱码
		mysql> insert into student values(1,'张三',18);
	--修改记录:update 表名 set 字段名 = 字段值
		mysql> update student set age=100;
		mysql> update student set name='tony'; #字段如果是字符串要加'??'
	--删除记录:delete from 表名
		mysql> delete from student;		

三,字段的约束
–1,主键约束
–是指可以给指定的字段添加一个主键约束,来达到 主键的值是唯一的+不能位空的效果
–mysql> create table a(id int);#id不是主键可以重复可以为空
–mysql> create table b(id int primary key);#id是主键必须唯一不能为空
–mysql> create table c(id int primary key auto_increment);#主键自动递增(从1开始)
–2,非空约束
–是指给特定的字段,添加非空约束,达到 字段值不能为空 的效果
–mysql> create table d(id int primary key auto_increment,
-> name varchar(10) not null #not null就是对name字段的非空约束
-> );
–3,唯一约束
–是指给字段加上唯一约束,达到 字段的值必须唯一 的效果
–mysql> create table e(id int primary key auto_increment,
-> age int not null unique #unique是对age字段的值的唯一约束
-> );
day02
一,基础函数
–1,概述
MySQL提供了很多函数,可以快速操作数据.
–2,各种函数的使用
–基本函数:lower\upper\length\substr\concat\replace\ifnull\uuid
–小数的函数:round\ceil \floor
–日期的函数:now\year\ month \ day\hour\minute \second\转义字符
–练习
#查看表结构
desc dept;
select * from dept;
#MySQL的函数们
select * from dept; #查询所有字段的值-低效
select dname from dept; #只查询dname字段的值-高效
select dname,deptno from dept; #只查询dname,deptno字段的值-高效
select dname,lower(dname) from dept;#lower(参数)-把参数变小写
select dname,upper(dname) from dept;#upper(参数)-把参数变大写
select dname,upper(dname),lower(dname) from dept;#查3列
#length(参数)-获取参数的长度
select dname,length(dname),loc,length(loc) from dept;
#substr(1,2)-1是要截取谁2是从哪儿开始(从1开始)
select dname,substr(dname,3) from dept;
#substr(1,2,3)-1是要截取谁2是从哪儿开始(从1开始)3是截取几个
select dname,substr(dname,2,3) from dept;
#concat()-拼接字符串
select dname,concat(dname,‘hello’) from dept;
select dname,concat(dname,“world”) from dept;
select dname,concat(dname,“world”,“123”) from dept;
#replace(1,2,3)替换-1是要替换的字段是谁2是要被替换的字符是啥3是新字符
#Mysql里整数可以简写,字符必须加’'或者""
select dname,replace(dname,‘c’,6) from dept;
select * from emp;
#ifnull(1,2)-1是字段名2是要被替换成的值
#ifnull如果是null可以替换成新值
select comm,ifnull(comm,1) from emp;

		#对小数的处理
		#round(四舍五入) & ceil(向上取整) & floor(全舍)
		select comm,ceil(comm),floor(comm),round(comm) from emp;
		#对日期数据的操作
		select now() ; #获取年月日时分秒
		select CURDATE(); #获取年月日
		select CURTIME(); #获取时分秒
		#year() month() day()
		select now(),year(now()),month(now()),day(now())
		#hour() minute() second()
		select now(),hour(now()),minute(now()),second(now())
		select uuid() #得到32位的字符
		select 'xian'
		select 'xi\'an'#'是SQL里的特殊符号,想作为普通字符使用需要转义\

–3,条件查询
–1,distinct/where/like/order by/limit
–2,练习
select ‘xian’
select ‘xi’an’#'是SQL里的特殊符号,想作为普通字符使用需要转义
select distinct loc from dept; #给字段的值去重
select * from emp;#查了所有
#给查询结果 加一个过滤条件
select * from emp where 字段名=字段值;#查id=100的数据
select * from emp where id=100;#查id是100的数据
select * from emp where ename=‘jack’ ;#查ename是jack的数据
select id,job,sal from emp where ename=‘jack’#查ename是jack的id,job,sal数据
select ename,job from emp where job=‘副总’;#副总的名字
#查询id>200的所有数据
select * from emp where id>200
#查询id>200的所有数据的名字和工资总和
select sal,comm,ename,sal+ifnull(comm,0) from emp where id>200
select * from emp where true
#SQL执行的顺序:from > where > select
select * from emp where 1=1
#多个条件间的关系: 并且and 或者or
#查询id是100,ename是jack的记录
select * from emp where id=100 or ename=‘jack’
select * from emp where job=‘副总’ and sal>10000
select * from emp where job=‘副总’ or sal>10000
#查询sal是8000,10000,20000的记录
select * from emp where sal=8000 or sal=10000 or sal=20000
select * from emp where sal in(8000,10000,20000)#等于8000或者等于10000或者等于20000
select * from emp where sal not in(8000,10000,20000)#不等于8000或者等于10000或者等于20000
#like 模糊查询
select * from emp where ename like ‘a’;#条件相当于ename=‘a’
select * from emp where ename like ‘%a%’;#中间包含着a
select * from emp where ename like ‘t%’;#以t开始后面有几个都行
select * from emp where ename like ‘%y’;#以y结束前面有几个都行

	#按照null过滤数据
	select * from emp where comm is null #过滤出来是null的记录
	select * from emp where comm is not null#过滤出来不是null的记录

	#between and 
	select * from emp where sal>8000 and sal<20000
	#过滤工资在[8000,20000]的
	select * from emp where sal between 8000 and 20000#[8000,20000]
	#limit 分页技术
	select * from emp limit 3 #只取前三条
	select * from emp limit 1,3 #从第二条开始取,取总共3条
	select * from emp limit 2,2 #从第三条开始取,取总共2条
	#order by 排序
	select * from emp order by sal #默认升序,按数字的从小到大
	select * from emp order by comm #默认升序,null放最上面
	select * from emp order by ename #默认升序,按自然顺序
	select * from emp order by hiredate #默认升序,按自然顺序
	select * from emp ORDER BY sal desc #默认是升序,desc降序
	#取工资最高的前3个人
	select * from emp ORDER BY sal desc limit 3;#desc降序排序
	#SQL的执行顺序? from > ORDER BY > limit
	select * from emp ORDER BY sal desc limit 0,3;#从第一条开始取,取3条
	#查询岗位是副总的 工资最高的员工信息
	#SQL的执行顺序? from > where >  order by > limit
	select * from emp where job='副总' order by sal desc limit 1

	#2017年以前入职的员工信息
	select * from emp where year(hiredate) < 2017
	select * from emp where hiredate < '2017-1-1'
	#2017-1-1到2021-5-1入职的员工信息
	select * from emp where hiredate <= '2021-5-1' and hiredate >= '2017-1-1'
	#公司福利不错13薪,统计员工的年薪
	select ename,job,sal,comm,(sal+ifnull(comm,0))*13 from emp 

–4,聚合函数
–对一列的结果进行运算
–count \ sum \avg \max \min
–练习
#count \sum \avg \max \min
select * from emp
select count(*) from emp #统计emp的记录总数-低效
select count(1) from emp #高效
select count(id) from emp #高效
select sal from emp
select max(sal) from emp #获取sal的最大值
select min(sal) from emp #获取sal的最小值
select avg(sal) from emp #获取sal的平均值
select sum(sal) from emp #获取sal的求和
#获取每个部门的,最高薪
select deptno,min(sal),max(sal) from emp group by deptno
#分组–聚合函数以外的列都需要分组
扩展:
–1,常见错误:
–表里有3列,赋值时只给两列赋值
mysql> insert into dept2 value(‘研发部’,‘北京’); #错误
ERROR 1136 (21S01): Column count doesn’t match value count at row 1
mysql> insert into dept2 value(null,‘研发部’,‘北京’);#正确

	--中文乱码,本来应该是正确的数据,但是说长度太大
		mysql> insert into dept2 value(null,'研发部','北京');
		ERROR 1406 (22001): Data too long for column 'dname' at row 1
		mysql> set names gbk; #防止中文乱码,后面插入的就不乱码了...
	--未知的列
		[SQL]select dname,concat(dname,hello) from dept;
		[Err] 1054 - Unknown column 'hello' in 'field list'

day03
一,分组
–1,概述
把数据按照一个规则划分成一个一个的组
对分组后的数据进行过滤就用固定搭配having
–2,语法
group by 字段 having 要过滤的字段
–3,练习
#---------------分组-----------
select * from emp;
select * from dept;
#最高薪资和平均薪资
select max(sal),avg(sal) from emp;
select job from emp;
#获取每个岗位的最高薪和平均薪
select max(sal),avg(sal),job from emp GROUP BY job;
#获取每个部门的最高薪和平均薪
select max(sal),avg(sal),deptno from emp GROUP BY deptno;
#给分组后的结果加过滤条件 where
select max(sal),avg(sal),deptno from emp
where deptno=1 #对select的结果过滤
group by deptno
#执行顺序:from > group by > having > select
select max(sal),avg(sal),deptno from emp
group by deptno
having deptno = 1

		#查询岗位是副总的最高薪和平均薪
		select max(sal),avg(sal) from emp where job='副总' 

		#执行顺序? from > where > group by > select
		select max(sal),avg(sal),job from emp 
		where job='副总'  #from表之后过滤表里的记录
		group by job
		#执行顺序? from > group by > having > select
		select max(sal),avg(sal),job from emp  
		group by job
		having job='副总'
		#group by是分组,要过滤固定搭配having

		#平均工资小于10000的部门
		select avg(sal),deptno from emp
		group by deptno #按照非聚合的列分组
		having avg(sal) < 15000 ; #ok
		#having deptno < 15000 ; #ok
		#having sal < 15000 ; #no ok
		#having job < 15000 ; #no ok


		#列名 也可以使用别名代替,语法:列名 别名
		select avg(sal) avg,deptno from emp
		group by deptno #按照非聚合的列分组
		having avg < 10000 ; #过滤条件

		#deptno出现的次数
		select deptno,count(deptno) X from emp
		group by deptno #非聚合列需要分组
		#2号deptno出现的次数
		#having deptno=2 #ok
		having X=2 #ok
		#having job=2 #no ok
		#having id=2 #no ok

二,事务
–1,概述
为了保证多条SQL语句要么同时成功,要么就同时失败
MySQL默认就开启了事务,一条SQL一个事务
如果想实现多条SQL在一个事务里执行,只能手动管理事务
开启事务:start transaction/begin
结束事务:commit(成功就提交,失败就自动回滚)/rollback(回滚)
–2,事务的4个特性
ACID
A是原子性–是指多条SQL要么都成功要么都失败
C是一致性–总和守恒
I是隔离性–数据库允许高并发,同时增删改查数据
D是持久性–是指对数据的操作持久有效
–3,事务的隔离级别
读未提交–效率最高,但是安全性最差(多并发时数据可能出错)
读已提交–效率较低,但是安全性较高(oracle数据库的默认级别)
可重复读–效率较低,但是安全性较高(MySQL数据库的默认级别)
串行 – 效率最低,安全性最高(实现表级锁)
–4,测试
#MySQL的事务
#MySQL会自动开启事务管理.一条SQL一条SQL的执行
#非要实现多条SQL在一个事务里,只能手动开启事务和结束事务
insert into dept values(null,‘财务部’,‘北京’)
insert into dept values(null,‘运营部’,‘上海’)
insert into dept values(null,‘人事部’,‘北京’)
select * from dept order by deptno desc
#手动管理事务
BEGIN;
insert into dept values(null,‘财务部X’,‘北京’);
insert into dept val(null,‘运营部2’,‘上海’);
insert into dept values(null,‘人事部2’,‘北京’);
commit;#提交事务–如果都正确就提交数据,如果错误数据回滚
#rollback;#回滚事务–不管SQL正确与否都会回滚–用的少

		SELECT @@tx_isolation;#REPEATABLE-READMySQL的默认值可重复读

day04
一,索引
–1,概述
为了提交数据的查询效率,可以给指定的列加索引
哪些列需要加索引?–常用来做查询条件的就加索引
分为几种:单值索引(一个索引只有一个列)
唯一索引(索引列的值必须唯一)
复合索引(一个索引可以包含多个列)
–2,常用SQL
–创建索引
create index 索引名 on 表名(列名)
–查看索引,主键会自动创建索引
SHOW INDEX FROM 表名
–删除索引
ALTER TABLE 表名 DROP INDEX 索引名
–练习:
#给teachers表的tname加索引,提高按照名字查询时的效率
#创建索引
create index tname_index on teachers(tname);
#查看索引
show index from teachers;
#删除索引
alter table teachers drop index tbirthday;
#添加唯一索引
desc teachers;
alter table teachers add UNIQUE(prof);#检查索引列的值必须唯一
#添加复合索引
alter table teachers add index many_index(tno,tname)

二,关联查询
–1,完成多表的关联查询,要知道查哪些表,表之间有什么关系
–2,练习
select * from dept;
select * from emp;
#笛卡尔积–是指多表联查,但是低效,因为查出来数据太多
select * from emp,dept;
#给笛卡尔积数据加过滤条件–就是指多表的关联关系
select * from emp,dept where emp.deptno = dept.deptno;#两张表的关系
select * from emp a,dept b where a.deptno = b.deptno;#使用别名
#查询students和scores表,其中sno=101的数据
select a.sno,a.sname,b.degree from students a,scores b
where a.sno=b.sno #两张表的关
and a.sno=101 #过滤条件

		#查询students和scores表 --inner join/left join/right join
		select * from scores a
		inner join scores b 
		on a.sno=b.sno #表间的关联关系
		where a.sno=101 #过滤结果

		#查询emp和dept表,过滤dname='research'的数据
		#笛卡儿积方式
		select * from dept,emp 
		where dept.deptno=emp.deptno#表间的关联关系
		and dept.dname='research';
		#关联查询方式
		select * from dept 
		inner join emp 
		on dept.deptno=emp.deptno#表间的关联关系
		where dept.dname='research'

		#子查询方式--把一次查询结果作为一张表来使用
		#查询research部门的员工信息
		#先查部门名称是research的部门编号
		select deptno from dept where dname='research'
		#再根据查到的部门编号查emp表
		#select * from emp where deptno=2
		select * from emp where deptno=(
				select deptno from dept where dname='research'
			)

		select * from students inner join 
		scores on  students.sno=scores.sno
		where  degree>90

		#练习子查询:#查询degree>90的学生信息
		#笛卡尔积方式
		select * from scores,students #表名
		where scores.sno=students.sno #两个表里的关联字段
		and scores.degree>90 #过滤条件
		#join方式
		select * from scores  #表名1
		inner join students  #表名2
		on  scores.sno=students.sno #两个表里的关联字段
		where scores.degree>90 #过滤条件
		#子查询方式
		select * from students #表名1
		where sno in(  #过滤条件
			select sno from scores where degree>90 #子查询
		)
		#练习子查询:#查询能讲高等数学的老师的信息
		select * from teachers where tno =(
			#先查courses表里老师的编号,再根据编号查teachers表
			select tno from courses where cname='高等数学'
		)
		select * from teachers a,courses b
		where a.tno=b.tno and bame='高等数学'

		#练习子查询:#查询部门地址在二区的员工信息
		select * from emp where deptno in(
				select deptno from dept where loc='二区'
		)

三,SQL练习题:
#查询所有记录
select * from teachers #低效
#只查询指定列
select tname,tsex from teachers #高效
#查询tno为804的记录
select * from teachers where tno=804
#模糊查询记录
select * from teachers where prof like ‘%教%’
#查询之间范围之间的所有记录
select * from teachers where tno BETWEEN 800 and 820
select * from teachers where tno>= 800 and tno<= 820
select * from teachers where tno in (804,820) #tno必须是804或者是820
# 查询满足两个条件的记录
select * from teachers where tsex=‘男’ and depart=‘计算机系’
select * from teachers where tsex=‘男’ or depart=‘计算机系’
#查询所有岗位
select distinct prof from teachers #distinct去掉重复值
#查询男老师的信息
select * from teachers where tsex=‘男’
#按tbirthday升序查询记录
select * from teachers order by tbirthday #默认是升序asc
#以tno升序、tbirthday降序查询记录
select * from teachers order by tno asc,tbirthday desc
# 查询总人数
select count(*) from teachers #低效
select count(1) from teachers #高效
# 查询各个系的人数
select depart,count(1) from teachers group by depart#非聚合列必须分组
# 查询至少有2人的岗位是啥
select prof,count(1) from teachers group by prof having count(1)>=2
# 查询记录中最年长和最年轻 #获取到出生日期数字越大年龄越小
select min(tbirthday),max(tbirthday) from teachers
# 查询大于平均年龄的记录
select * from teachers
#where里不能用聚合函数
where tbirthday < (
select avg(tbirthday) from teachers #子查询
)
#查询年龄最大的老师信息
select min(tbirthday) from teachers
#查询每个cno的最高分
select cno,max(degree) from scores group by cno
#查询每个cno的平均分
select * from scores group by cno
#查询cno='3-105’的课程信息
select * from courses where cno=‘3-105’
#查询scores表里出现过的课程信息
select * from courses where cno in (
select DISTINCT cno from scores
)
#查询没划分部门的员工信息
select * from emp where deptno is null
#查询同名的员工记录
#select * from emp where count(ename) > 1
select ename from emp GROUP BY ename having count(ename) > 1
#全部学生按出生日期排序
select * from students ORDER BY sbirthday #默认升序(年龄从大到小)
#每个班上最小年龄的学员
select class,max(sbirthday) from students GROUP BY class

	#查询学生的姓名和年龄
	select sname,year(now())-year(sbirthday) as age from students
	#查询男教师及其所上的课程
	select * from teachers a,courses b where a.tno=b.tno and a.tsex='男'

	#查询得分前3名的分数信息
	select * from scores order by degree desc limit 3

day07
一,JDBC
–1,代码
package cn.tedu;

	import java.sql.*;
	import java.util.Scanner;

	//测试 JDBC
	//JDBC用来把java程序和数据库连接起来,目的是通过java代码操作数据库
	public class TestJdbc {
	    public static void main(String[] args) throws Exception {
	//        method();//普通方式
	//        method2();//暴露SQL注入问题
	        method3();//解决SQL注入问题
	    }
	    //需求:按照 用户名 和 密码 查询用户信息
	    private static void method3() throws Exception{
	        //注册驱动
	        Class.forName("com.mysql.jdbc.Driver");
	        //获取连接
	String url="jdbc:mysql://localhost:3306/jdbctest?characterEncoding=utf8";
	        String name="root";
	        String pwd="root";
	        Connection conn = DriverManager.getConnection(url,name,pwd);
	        //获取传输器Statement,执行SQL
	//        Statement st = conn.createStatement();//SQL注入攻击问题
	//        String sql = "select * from user where name='"+a+"' and pwd='"+b+"'";

	        //获取传输器PrepareStatement,执行SQL
	        //?叫做占位符,PreparedStatement只把SQL骨架发送给数据库
	        String sql = "select * from user where name=? and pwd=?";
	        PreparedStatement ps = conn.prepareStatement(sql);
	        //给SQL设置参数
	        ps.setString(1,"jack");//第一个?设置参数
	        ps.setString(2,"123");//第二个?设置参数
	        //执行SQL
	        ResultSet rs = ps.executeQuery();
	        //解析结果集
	        while(rs.next()){
	            for (int i = 1; i <= 3 ; i++) {
	                //按照索引查
	                System.out.println(rs.getString(i));
	            }
	        }
	        //释放资源
	        rs.close();
	        ps.close();
	        conn.close();
	    }
	    //暴露SQL注入问题--发生了SQL拼接
	    //需求:按照 用户名 和 密码 查询用户信息
	    private static void method2() throws Exception {
	        //中文查不出来数据:
	//String url="jdbc:mysql://localhost:3306/jdbctest?characterEncoding=utf8";
	        //注册驱动
	        Class.forName("com.mysql.jdbc.Driver");
	        //获取连接
	        String url="jdbc:mysql://localhost:3306/jdbctest";
	        String name="root";
	        String pwd="root";
	        Connection conn = DriverManager.getConnection(url,name,pwd);
	        //获取传输器Statement,执行SQL
	        Statement st = conn.createStatement();

	        //TODO 测试SQL注入攻击问题
	        //是指当用户只输入数据时,恶意包含了特殊的SQL语句,改变了SQL语义
	        //导致只需要名字不需要密码也可以查看所有数据(数据泄露)
	        //名字输入后面两种值,都会产生SQL攻击问题: jack'# 或者 jack' or '1=1
	        String a = new Scanner(System.in).nextLine();
	        String b = new Scanner(System.in).nextLine();
	String sql = "select * from user where name='"+a+"' and pwd='"+b+"'";
	        //执行SQL
	        ResultSet rs = st.executeQuery(sql);
	        //解析结果集
	        while(rs.next()){
	            for (int i = 1; i <= 3 ; i++) {
	                //按照索引查
	                System.out.println(rs.getString(i));
	            }
	        }
	        //释放资源
	        rs.close();
	        st.close();
	        conn.close();
	    }
	    //使用JDBC查询user表的数据
	    private static void method() throws Exception {
	        //1,注册驱动(jar包)
	        Class.forName("com.mysql.jdbc.Driver");
	        //2,连接数据库
	        //getConnection(1,2,3)-1是要连接哪个数据-2是用户名-3是密码
	        Connection con = DriverManager.getConnection(
	//                "协议//服务器的名字:数据库的端口号/数据库名",
	//                "jdbc:mysql://localhost:3306/jdbctest",
	                "jdbc:mysql:///jdbctest",
	                    //url简写(使用本机使用默认的端口号)
	                    "root","root");
	        //3,获取传输器Statement
	        Statement st = con.createStatement();
	        //4,执行SQL
	        String sql = "select * from user";
	        ResultSet rs = st.executeQuery(sql);
	        //5,解析结果集
	        while(rs.next()){ //next()判断有数据吗
	            //有数据就一个一个解析id/name/pwd
	            for (int i = 1; i <= 3; i++) {
	                System.out.println( rs.getString(i) );//按索引查;
	            }

	            String id2 = rs.getString("id");//按索引查
	            String name2 = rs.getString("name");//按索引查
	            String pwd2 = rs.getString("pwd");//按索引查
	            System.out.println(id2+name2+pwd2);
	        }
	        //6,释放资源
	        rs.close();
	        st.close();
	        con.close();
	    }
	}

--2,工具类
	package cn.tedu;

	import java.sql.Connection;
	import java.sql.DriverManager;
	import java.sql.PreparedStatement;
	import java.sql.ResultSet;

	public class JDBCUtils {
	    /**
	     * 获取 数据库的连接
	     * @return Connection
	     * @throws Exception
	     */
	    public static Connection getConnection() throws Exception{
	        //注册驱动
	        Class.forName("com.mysql.jdbc.Driver");
	        //获取连接
	        String url="jdbc:mysql://localhost:3306/jdbctest?characterEncoding=utf8";
	        String name="root";
	        String pwd="root";
	        Connection conn = DriverManager.getConnection(url,name,pwd);
	        return conn;
	    }

	    /**
	     * 关闭JDBC的资源
	     * @param rs 结果集
	     * @param ps 传输器
	     * @param conn 连接
	     */
	    public static void close(ResultSet rs, PreparedStatement ps,Connection conn){
	        if(rs != null){//为了防止空指针异常
	            try{
	                rs.close();
	            }catch (Exception e){
	                e.printStackTrace();
	            }finally { //为了close时又异常
	                rs = null;
	            }
	        }
	        if(ps != null){//为了防止空指针异常
	            try{
	                ps.close();
	            }catch (Exception e){
	                e.printStackTrace();
	            }finally { //为了close时又异常
	                ps = null;
	            }
	        }
	        if(conn != null){//为了防止空指针异常
	            try{
	                conn.close();
	            }catch (Exception e){
	                e.printStackTrace();
	            }finally { //为了close时又异常
	                conn = null;
	            }
	        }
	    }


	}

--3,改造代码
	package cn.tedu;

	import java.sql.*;
	import java.util.Scanner;

	//测试 JDBC
	//JDBC用来把java程序和数据库连接起来,目的是通过java代码操作数据库
	public class TestJdbc {
	    public static void main(String[] args) throws Exception {
	//        method();//普通方式
	//        method2();//暴露SQL注入问题
	        method3();//解决SQL注入问题
	    }
	    //需求:按照 用户名 和 密码 查询用户信息
	    private static void method3(){
	        Connection conn = null;
	        PreparedStatement ps = null;
	        ResultSet rs = null;
	        try {
	            //TODO  改造 调用工具类,获取和数据库的连接
	            conn = JDBCUtils.getConnection();
	            //获取传输器Statement,执行SQL
	//        Statement st = conn.createStatement();//SQL注入攻击问题
	//        String sql = "select * from user where name='"+a+"' and pwd='"+b+"'";

	            //获取传输器PrepareStatement,执行SQL
	            //?叫做占位符,PreparedStatement只把SQL骨架发送给数据库
	            String sql = "select * from user where name=? and pwd=?";
	            ps = conn.prepareStatement(sql);
	            //给SQL设置参数
	            ps.setString(1, "jack");//第一个?设置参数
	            ps.setString(2, "123");//第二个?设置参数
	            //执行SQL
	            rs = ps.executeQuery();
	            //解析结果集
	            while (rs.next()) {
	                for (int i = 1; i <= 3; i++) {
	                    //按照索引查
	                    System.out.println(rs.getString(i));
	                }
	            }
	        }catch (Exception e){
	            e.printStackTrace();
	        }finally{ //释放资源,一定要被执行 !!!
	           //调用工具,完成关闭资源
	            JDBCUtils.close(rs,ps,conn);
	        }
	    }
	    //暴露SQL注入问题--发生了SQL拼接
	    //需求:按照 用户名 和 密码 查询用户信息
	    private static void method2() throws Exception {
	        //中文查不出来数据:
	//String url="jdbc:mysql://localhost:3306/jdbctest?characterEncoding=utf8";
	        //注册驱动
	        Class.forName("com.mysql.jdbc.Driver");
	        //获取连接
	        String url="jdbc:mysql://localhost:3306/jdbctest";
	        String name="root";
	        String pwd="root";
	        Connection conn = DriverManager.getConnection(url,name,pwd);
	        //获取传输器Statement,执行SQL
	        Statement st = conn.createStatement();

	        //TODO 测试SQL注入攻击问题
	        //是指当用户只输入数据时,恶意包含了特殊的SQL语句,改变了SQL语义
	        //导致只需要名字不需要密码也可以查看所有数据(数据泄露)
	        //名字输入后面两种值,都会产生SQL攻击问题: jack'# 或者 jack' or '1=1
	        String a = new Scanner(System.in).nextLine();
	        String b = new Scanner(System.in).nextLine();
	String sql = "select * from user where name='"+a+"' and pwd='"+b+"'";
	        //执行SQL
	        ResultSet rs = st.executeQuery(sql);
	        //解析结果集
	        while(rs.next()){
	            for (int i = 1; i <= 3 ; i++) {
	                //按照索引查
	                System.out.println(rs.getString(i));
	            }
	        }
	        //释放资源
	        rs.close();
	        st.close();
	        conn.close();
	    }
	    //使用JDBC查询user表的数据
	    private static void method() throws Exception {
	        //1,注册驱动(jar包)
	        Class.forName("com.mysql.jdbc.Driver");
	        //2,连接数据库
	        //getConnection(1,2,3)-1是要连接哪个数据-2是用户名-3是密码
	        Connection con = DriverManager.getConnection(
	//                "协议//服务器的名字:数据库的端口号/数据库名",
	//                "jdbc:mysql://localhost:3306/jdbctest",
	                "jdbc:mysql:///jdbctest",
	                    //url简写(使用本机使用默认的端口号)
	                    "root","root");
	        //3,获取传输器Statement
	        Statement st = con.createStatement();
	        //4,执行SQL
	        String sql = "select * from user";
	        ResultSet rs = st.executeQuery(sql);
	        //5,解析结果集
	        while(rs.next()){ //next()判断有数据吗
	            //有数据就一个一个解析id/name/pwd
	            for (int i = 1; i <= 3; i++) {
	                System.out.println( rs.getString(i) );//按索引查;
	            }

	            String id2 = rs.getString("id");//按索引查
	            String name2 = rs.getString("name");//按索引查
	            String pwd2 = rs.getString("pwd");//按索引查
	            System.out.println(id2+name2+pwd2);
	        }
	        //6,释放资源
	        rs.close();
	        st.close();
	        con.close();
	    }
	}
三,SQL优化
	--1,查询语句里,用字段名代替* 
			select id,name,age from user
	--2,过滤条件尽量不要用or,难以避免
			select id,name,age from user where id=1 or name='123'
	--3,表设计时,字段的类型,最好用varchar代替char
			--char是固定长度,可能浪费空间,varchar长度可变
	--4,模糊查询,最好先确定前面的数据
			select id,name,age from user where name like 't%'
	--5,字段的值,尽量用数字不用字符串
			--查的快,省空间,1个数字只占一个字符,但是一个汉字要占3个字符(utf8)	
	--6,查询的结果范围尽量小,数据量尽量少
			select id,name,age from user where name='123' and id=3
	--7,给字段加索引
			#如果name有索引,由于123这个值写的不规范,会导致索引失效
			select id,name,age from user where name=123 
	--8,每个列都可以加索引,但是不能太多,最好不能超过5个
	--9,对字段的值可以去重,使用distinct关键字,但是不要对太多列去重
			select distinct * from user
	--10,过滤条件里最好不要用!=  <>  
	--11,尽量给null列设置默认值
	--12,批量操作
			--需要一次性删除多条(10000)时,最好分批删除(按照500个的方式删除多次)
			--增加大量数据时,可以只发起一个insert语句,来减少事务的开启和结束
				insert into user(id) values(1),(2),(3),(4),(5),(6),(7)
	--13,group by
			--尽量把数据的范围控制到最小然后再分组		
			select id,count(*) from user group by id having id > 100 #低效
			select id,count(*) from user where id > 100 group by id   #高效
	--14,伪删除设计
			真实的开发中,很少真正的删除数据,而是把要删除的数据改变状态1 -> 0


lose();
st.close();
con.close();
}
}
三,SQL优化
–1,查询语句里,用字段名代替*
select id,name,age from user
–2,过滤条件尽量不要用or,难以避免
select id,name,age from user where id=1 or name=‘123’
–3,表设计时,字段的类型,最好用varchar代替char
–char是固定长度,可能浪费空间,varchar长度可变
–4,模糊查询,最好先确定前面的数据
select id,name,age from user where name like ‘t%’
–5,字段的值,尽量用数字不用字符串
–查的快,省空间,1个数字只占一个字符,但是一个汉字要占3个字符(utf8)
–6,查询的结果范围尽量小,数据量尽量少
select id,name,age from user where name=‘123’ and id=3
–7,给字段加索引
#如果name有索引,由于123这个值写的不规范,会导致索引失效
select id,name,age from user where name=123
–8,每个列都可以加索引,但是不能太多,最好不能超过5个
–9,对字段的值可以去重,使用distinct关键字,但是不要对太多列去重
select distinct * from user
–10,过滤条件里最好不要用!= <>
–11,尽量给null列设置默认值
–12,批量操作
–需要一次性删除多条(10000)时,最好分批删除(按照500个的方式删除多次)
–增加大量数据时,可以只发起一个insert语句,来减少事务的开启和结束
insert into user(id) values(1),(2),(3),(4),(5),(6),(7)
–13,group by
–尽量把数据的范围控制到最小然后再分组
select id,count() from user group by id having id > 100 #低效
select id,count(
) from user where id > 100 group by id #高效
–14,伪删除设计
真实的开发中,很少真正的删除数据,而是把要删除的数据改变状态1 -> 0

本文标签: mysqlText