Mysql数据库学习笔记[完结]

本文深入讲解了数据库的分类,如关系型与非关系型,并重点介绍了MySQL数据库的结构、字符集设置和SQL语言的DML、DDL和DCL。涵盖了创建表、数据结构、SQL语句、索引优化、范式理论以及JDBC和SQL注入等内容,适合数据库管理和SQL初学者阅读。
摘要由CSDN通过智能技术生成

一、数据库

1.概述

  • 数据库(database ,简称db)就是用来存储数据和管理数据的仓库
    • 分类:
      • 关系型数据库:指存放的数据之间是有紧密关系的
        • 常见的有:Oracle、MySQL、SQLServer、Access
      • 非关系型数据库:指存放的数据之间关系松散
        • 常见的有:MongoDB、Redis、Solr、ElasticSearch、Hive、HBase
  • 关系型数据库
    关系型数据库有特定的组织方式,其以行和列的形式存储数据,以便于用户理解。关系型数据库这一系列的行和列被称为表,一组表组成了数据库。用户通过查询来检索数据库中的数据,而查询是一个用于限定数据库中某些区域的执行代码。关系模型可以简单理解为二维表格模型,而一个关系型数据库就是由二维表及其之间的关系组成的一个数据集合。

2.Mysql数据库

  • 服务端:负责实际的数据的增删改查的操作的。
    • 默认端口号是3306
    • 如果需要存储中文建议指定utf8或gbk这样支持中文的字符集
  • 客户端:连接服务器,对数据进行增删改查的交互的界面
    • 学习时可以使用MariaDB 百度介绍
    • DOS命令提示符(win:开始-支行-cmd)
      • mysql -root -p 回车后,待Enter password:提示出现后输入密码即可
      • 或直接输入mysql -uroot -proot 此种方式是-u后面直接跟用户名,没有空格,同样的-p后面直接跟密码
    • 可视化工具
      • SQLyog
      • Navicat
      • MysqlFront

3.数据的结构

(1). 结构

数据库 -> 表 -> 记录
数据库:对库的操作
数据表:维护表的结构,如包含哪些列、叫什么名、什么类型、多大、各种结束等
记录:按表的结构来增删改查数据

官方文档
运算符
数据类型
标识符

(2).命名规则

数据类型:https://mariadb.com/kb/en/data-types/

标识符

字段名必须以字母开头,尽量不要使用拼音
长度不能超过30个字符(不同数据库,不同版本会有不同)
不能使用SQL的保留字,如where,order,group
只能使用如下字符az、AZ、0~9、$ 等
Oracle习惯全大写:USER_NAME,mysql习惯全小写:user_name
多个单词用下划线隔开,而非java语言的驼峰规则

(3).数据类型
  • 字符
    char长度固定,不足使用空格填充,最多容纳2000个字符,char(11)存储abc,占11位。查询速度极快但浪费空间
    varchar变长字符串,最多容纳4000个字符,varchar(11)存储abc,只占3位。查询稍慢,但节省空间。Oracle为varchar2
    大文本: 大量文字(不推荐使用,尽量使用varchar替代)

  • 数字
    tinyint,int整数类型
    float,double小数类型
    numeric(5,2) decimal(5,2)—也可以表示小数,表示总共5位,其中可以有两位小数
    decimal和numeric表示精确的整数数字

  • 日期
    date 包含年月日
    time时分秒
    datetime包含年月日和时分秒
    timestamp时间戳,不是日期,而是从1970年1月1日到指定日期的毫秒数

  • 图片
    blob 二进制数据,可以存放图片、声音,容量4g。早期有这样的设计。但其缺点非常明显,数据库庞大,备份缓慢,这些内容去备份多份价值不大。同时数据库迁移时过大,迁移时间过久。所以目前主流都不会直接存储这样的数据,而只存储其访问路径,文件则存放在磁盘上。

二、SQL语言

1.定义

  • 结构化查询语言(Structure Query Language)简称SQL,是一种特殊目的的编程语言,是一种数据库查询和程序设计语言,用于存取数据、管理数据库及表;同时也是数据库脚本的扩展名。
  • SQL 是1986.10月由美国国家标准局ANSI通过的数据库语言美国标准,接着国际标准化组织ISO公布了SQL正式国际标准

2.分类

  • DML(Data Manipulation Language) 数据操纵语言,如insert update delete select

https://mariadb.com/kb/en/data-manipulation/

SELECT INSERT UPDATE DELETE
  • DDL(Data Definition Language) 数据库定义语言

https://mariadb.com/kb/en/data-definition/

CREATE DATABASE

CREATE TABLE
ALTER TABLE
DROP TABLE

CREATE VIEW
ALTER VIEW
DROP VIEW

TRUNCATE TABLE
  • DCL(Data Control Language) 数据库查询语言,如grant deny revoke等,只有管理员才有相应的权限的
CONNECTGRANTDENY
SELECTINSERTUPDATEDELETE
EXECUTEUSAGEREFERENCES
  • DQL(Data Query Language)数据库查询语言
SELECT
  • TCL:事务控制语言。关键字:COMMIT、ROLLBACK、SAVEPOINT。

3.语句

数据库操作

show databases; 查看数据库
select * from information_schema.schemata;与上面一样的功能,只是信息更多一些
create database testdb default character set utf8 创建数据库 database后面跟要创建的数据库的名字,后面四个单词是用来指定数据库默认编码为utf8,也可以指定其他编码
show variables like "%char%"; 查看整个数据库的所有编码相关信息
show create database testdb; 查看指定数据库的编码
ALTER DATABASE testdb DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 修改指定数据库的编码
alter table [数据库名称.表名称] character set utf8; 修改指定数据表的编码
alter table [表名称] change [字段名称] [字段名称] [类型] character set utf8; 修改指定数据表中字段的编码
drop database testdb 删除数据库testdb

数据表操作

show tables; 查看当前数据库中的所有的表
select * from information_schema.tables where table_schema = 'science' and table_name like '%science%' limit 10; 查看指定数据库中有哪些名字中包含science中的表
desc testdb.ta; 查看指定表的结构

drop table ta; 删除指定表

创建数据表

CREATE TABLE `users` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(64) NULL DEFAULT NULL,
    `password` VARCHAR(64) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
) default COLLATE='utf8_general_ci'
  • 添加列
alter table testb add column money numeric(7,2);
  • 修改表名
rename table 原表名 to 新表名;
  • 插入数据
insert into users values(1,"张三","123"); --按原表里面的各列的顺序和数量来插入时,可以使用此种简写
insert into users(name,password) values("李四","23");--插入的列的数量或是顺序与表格不一致时,可以手动指定要插入的列的名称与顺序

set names gbk;--如果插入数据时,报中文字段值太长如"ERROR(22001):Data too long for column 'loc' at row 1"这个报错,就可以临时使用这个命令来临时解决
  • insert into if not exists问题
    即如果有需要批量插入的数据,但其中部分数据已存在,所以直接运行的话会报DUPLICATE KEY的错误提示,导致批处理无法执行。此时,可以使用如下语句,即在原有的insert语句的最后面加上ON DUPLICATE KEY UPDATE id = id,这里的id为表中的字段,任意字段都可以的,意思是如果插入时报DUPLICATE的错,则将id的值更新为原有的值,也就是不更新,也就实现了所谓的INSERT IF NOT EXISTS。当然了,这只是该语句的一个用法,详细用法移步官方文档:13.2.5.2 INSERT … ON DUPLICATE KEY UPDATE 语句
insert into users values(1,"张三","123") ON DUPLICATE KEY UPDATE id = id;

也可以使用ignore关键字

mysql> SET sql_mode = '';
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO t (i) VALUES('abc');
Query OK, 1 row affected, 1 warning (0.01 sec)

mysql> SHOW WARNINGS;
+---------+------+--------------------------------------------------------+
| Level   | Code | Message                                                |
+---------+------+--------------------------------------------------------+
| Warning | 1366 | Incorrect integer value: 'abc' for column 'i' at row 1 |
+---------+------+--------------------------------------------------------+
1 row in set (0.00 sec)

官方文档:https://dev.mysql.com/doc/refman/5.7/en/insert.html
https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#ignore-effect-on-execution

  • 删除数据
delete from users where id = 1;--删除users表中id=1的记录,如果不写where条件,则整个表中所有的数据全部被删掉

联表限制查询条件,如下为删除sys_role_permission表中无法与sys_role表中相匹配的数据

delete ta 
from sys_role_permssion as ta 
left join sys_role as tb on ta.roleId = tb.id 
where tb.id is null;

关于联表删除的还有其他写法,可看此文章:https://blog.csdn.net/xia_xing/article/details/47780091

字段约束

字段约束可以混搭

主键约束

主键列用于区分每条记录,因此需要整表唯一且不能为空

CREATE TABLE `users` (
    `id` INT(11)  PRIVMARY KEY,
    `name` VARCHAR(64) NULL DEFAULT NULL 
) 

AUTO_INCREMENT用于修饰int型的字段的值自动递增,Oracle中可设置自增策略

CREATE TABLE `users` (
    `id` INT(11) PRIVMARY KEY  AUTO_INCREMENT,
    `name` VARCHAR(64) NULL
)

当向表中定义为主键的列插入相同的值时,会遇到如下错误

ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
非空约束 not null

该字段不能为null,其他任意值都可以

CREATE TABLE tb_user(
id INT AUTO_INCREMENT,
NAME VARCHAR(30) UNIQUE NOT NULL,
age INT,
phone VARCHAR(20) UNIQUE NOT NULL,
email VARCHAR(30) UNIQUE NOT NULL,
PRIMARY KEY (id)
);

向定义了非空约束的列中插入null值时,会遇到如下错误

ERROR 1048 (23000): Column 'uid' cannot be null
唯一约束 unique

该字段的值必须整个表内唯一,不可重复,比如身份证号、手机号,每个人的都不能一样

CREATE TABLE tb_user(
id INT,
NAME VARCHAR(30) UNIQUE NOT NULL,
phone VARCHAR(20) UNIQUE NOT NULL,
email VARCHAR(30) UNIQUE NOT NULL,
PRIMARY KEY (id)
);

向表中定义了唯一约束的列中插入相同的值时,会遇到如下错误

ERROR 1062 (23000): Duplicate entry '1' for key 'uid'
默认约束
  • 设置该字段的默认值,使用default关键字,
create table e(id int primary key auto_increment,sex varchar(10) default '男')

这样插入时如果此列的值为默认值,则可以不这一列赋值

insert into a  (id)  values(1);#sex有默认值,添加记录时不给值的话就是默认值
外建约束
  • 创建外键约束后,被指向的表格称为主表,子表在新增记录时,外键字段的值必须是主表里面对应的键的列中已经存在了的,否则将无法新增成功。同样的,主表在想要删除一条记录时,也必须保证子表中没有任何一条记录与此记录相关,才可以删除。
  • references指向的列必须至少是unique的,主键自带unique,foreign key()里面的列应该是没有太多的要求
CREATE TABLE t_user(id INT PRIMARY KEY AUTO_INCREMENT,t_name VARCHAR(20));
create table tb_user_address(user_id int primary key,address varchar(255),foreign key(user_id) references tb_user(id));
# 定义了外键约束后,外键所在表插入被参考表里面没有的关键值时会得到如下错误
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`cgb2109`.`tb_user_address`, CONSTRAINT `tb_user_address_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `tb_user` (`id`))

# 被参考表里面想要删除`外键所在表中已经存在了的关键值`的记录时,会得到如下错误
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`cgb2109`.`tb_user_address`, CONSTRAINT `tb_user_address_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `tb_user` (`id`))

检查约束

很少使用,了解即可,录入age超过200将报错,使用check关键字

ERROR 4025 (23000): CONSTRAINT `tb_user .age` failed for `dm`.`tb_user ` #设置了检查约束后如果插非法数据,会得到此错误信息
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, #自增主键 
NAME VARCHAR(50) NOT NULL UNIQUE, #非空,唯一索引
sex CHAR(2) DEFAULT '男', #默认值
phone CHAR(18),
age INT,
CHECK (age>0 AND age<=200),
createdTime DATE DEFAULT NOW()
);
DESC tb_user;

常见函数

  • upper/lower 作用到列上,对某列使用此函数后,该列查询出来的结果将会全部转成大写/小写
select upper(dname) from dept;--查询所有的部门名称,并将部门名称里面的值转成大写
select lower(dname) from dept;--查询所有的部门名称,并将部门名称里面的值转成小写
  • length 作用到列上,计算查询结果在当前编码下的字节长度,注意:返回值是字节长度,比如在utf-8编码下,一个汉字占3个字节,因此“Java开发部”这个的长度就是13,与
select length(dname) from dept;--查询所有的部门名称,但只显示其字节数
  • substr(string,start,length) 截取字符串,从第start个位置开始,截取length个字符。start从1开始,这里是按字符,一个字母或一个中文都是一个字符,不够的就有多少显示多少。
#
SUBSTRING(str,pos), 
SUBSTRING(str FROM pos), 
SUBSTRING(str,pos,len),
SUBSTRING(str FROM pos FOR len)

SUBSTR(str,pos), 
SUBSTR(str FROM pos), 
SUBSTR(str,pos,len),
SUBSTR(str FROM pos FOR len)
#
select substr(dname,2,3);--只显示部门名称从第2个位置开始,共显示3个字符
  • concat(str1,str2,…) 拼接所有给定的字符串
select ename,concat(ename,123) from emp;--在每个ename的值的后面拼接上字符串123
  • group_concat(column_name order by column_name desc separator “|”) 聚合字符串,参考原连接
SELECT * FROM `wms_sku`;

SELECT order_id,group_concat(sku_name ORDER BY sku_name DESC SEPARATOR "|") AS skus FROM wms_sku GROUP BY order_id;

结果如下:
在这里插入图片描述
在这里插入图片描述

  • replace 替换文本
select dname,replace(dname,'j','Mr j') X from dept;--把j字符替换成Mr j
  • ifnull(column_name,value) 如果第一个参数里面的值是null,将替换成value的值
select ifnull(comm,3.13) as com from emp;--把comm列值是null的替换成3.13后显示
  • round() 四舍五入取整
select round(comm) from emp;--对薪水四舍五入处理
  • ceil() 向上取整
select ceil(comm) from emp;--对薪水向上取整
  • floor() 向下取整
select floor(comm) from emp;-- 对薪水向下取整
  • now() 取当前yyyy-MM-dd HH:mm:ss的时间
select now(),year(now()),month(now()),day(now()),hour(now()),minute(now()),second(now()); -- 分别获取当前时间、年、月、日、时、分秒
  • 转义字符 反斜线 \
select "a\'b";
  • dinstinct 去除当前列的重复项
SELECT DISTINCT loc FROM dept;-- 查询所有的部门,总共有几个办公地点

UUID 是一个 128 位的数字,由 utf8 字符串表示,aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee 格式为五个十六进制数字:

  • 前三个数字是从时间戳生成的。
  • 第四个数字保留时间唯一性,以防时间戳值失去单调性(例如,由于夏令时)。
  • 第五个数字是提供空间唯一性的 IEEE 802 节点编号。如果后者不可用(例如,由于主机没有以太网卡,或者我们不知道如何在操作系统上找到接口的硬件地址),则用随机数代替。在这种情况下,无法保证空间唯一性。尽管如此,碰撞的概率应该非常低。

条件查询

  • 别名(使用 AS 关键字)参考链接
    • SQL 别名用于为 表 或 表中的列 提供临时名称。
    • SQL 别名通常用于使 表名 或 列名 更具可读性。
    • SQL 一个别名只存在于查询期间。
  • where 条件子句,注意:不可以使用别名、聚合函数 参考连接

https://mariadb.com/kb/en/select/

select * from emp

select * from emp where 1=1 --类似没条件

select * from emp where 1=0 --条件不成立

select * from emp where empno=100 --唯一条件

select * from emp where ename='tony' and deptno=2 --相当于两个条件的&amp;关系

select * from emp where ename='tony' or deptno=1 --相当于两个条件的|关系

select name, sal from emp where sal=1400 or sal=1600 or sal=1800;

-- 或

select name, sal from emp where sal in(1400,1600,1800);

select name, sal from emp where sal not in(1400,1600,1800);

  • like 模糊查询 通配符%代表0到n个字符,通配符下划线_代表1个字符
select * from emp where ename like 'l%' --以l开头的

select * from emp where ename like '%a' --以a结束的

select * from emp where ename like '%a%' --中间包含a的

select * from emp where ename like 'l__' --l后面有两个字符的 _代表一个字符位置

  • null 筛选记录的某个值是否为null。注意:此处不使用=来比较,而是使用is。比如要查奖金为null的需要使用select * from emp where mgr is null 而不是select * from emp where mgr = null
select * from emp where mgr <=> null --过滤字段值为空的

select * from emp where mgr is null --过滤字段值为空的

select * from emp where mgr is not null --过滤字段值不为空的
  • between value1 and value2 值在value1(含)与value2(含)之间的
select * from emp where sal between 5000 and 10000;
  • limit([offset,]length) 限制返回结果的条数,从1+offset条记录开始,展示length条记录
LIMIT [offset, ]row_count #跳过offset条记录,返回接下来的row_count条记录
LIMIT row_count OFFSET offset#跳过offset条记录,返回接下来的row_count条记录

select * from emp limit 2;
  • order by 指定排序依据的列,asc升序(默认), desc 降序 ,如果排序的是汉字,则会按汉字对应的在UTF-8字符集里面的顺序来排序
select * from emp order by sal asc,mgr desc;

序号

@r :=@r + 1 as alias放在select 里面,然后在from的表的最后面加,(select @r:=0) as alias

#给查询结果加个序号
select @i:=@i+1,ta.* 
from dept as ta,(select @i:=0) as t;//其实后面这句就相当于是初始货@i这个变量并赋值

统计实例

  • 统计员工的年薪
select ename,sal,ifnull(sal,0)*13 as 年薪,comm,ifnull(comm,0)*13 as 年奖金 from emp;

聚合函数aggregation

官方文档

把一列所有的值聚合起来,然后做数据分析

  • count
    count(*) 与 count(1) 效果一致,后者更高效一些。count(sal)这种是统计不为null的数量
  • max/min 求最大值 、最小值
  • sum 求合
  • avg 求平均数
  • group by 当查询语句里面同时有聚合列和非聚合列时,需要使用group by 指定非聚合列是哪些。
    比如要统计各部门的人数这个问题,是在查询各部门人数的同时还要显示部门名称 ,这个时候就要group by 部门名称,这样每个部门名称都会有一行。注意:group by是可以使用列的虽名的。
SELECT YEAR(hiredate) AS ye ,COUNT(1) FROM emp GROUP BY YEAR(hiredate);
  • having 对查询出来的结果做最后的筛选,注意:having也是可以使用别名的
SELECT deptno,MAX(sal) FROM emp GROUP BY deptno HAVING MAX(sal)>10000; 

由于having过程是在聚合函数执行以后,所以如果需要对使用了非聚性质的函数的列进行筛选时,需要以如下的方式来指定列名

select year(ta.hiredate),count(1) #此时第一个列的列名已经变成了year(ta.hiredate)
from  emp as ta 
group by YEAR(ta.hiredate) 
having `year(ta.hiredate)`>2002;

窗口函数

窗口函数,也叫OLAP函数(Online Anallytical Processing,联机分析处理),可以对数据库数据进行实时分析处理。

个人理解窗口函数就是对查询出来的数据进行有序计算,比如我们用各种限定条件查询出来十天的销售记录,这十条记录是按日期排序的,如果希望每一行都能直接显示出从第一行到当前行累计的销售额,那么窗口函数就派上用场了
详细内容参考:https://www.cnblogs.com/SmithBee/p/16056458.html
还有一个有序计算的:https://www.jianshu.com/p/1a176fc50e7f
工作上遇到一个例子,就是需要计算当前行和上一行的差值,如果数据库版是5.7没有窗口函数的话,可以尝试下使用变量的方式
注意:5.7没有窗口函数

select empno,@i as lastEmpno,@i:=empno
from emp,(select @i:=-1) as ta
order by empno

可以得到这样的结果,也就达到目的了
但是!官方不推荐这样使用!原因是包含变量的表达式的计算顺序是没有定义的,即不保证能获得稳定的结果参考文档

However, the order of evaluation for expressions involving user variables is undefined.

empnolastEmpno@i:=empno
100-1100
200100200
300200300
400300400
500400500

语句的执行顺序

优化:小表驱动大表,因为from后面的表是注定要全表扫描的,所以可以把结构简单或是记录条数小的表格放到from后面会提高效率。

from 
join 
on 
where 
group by(开始使用select中的别名,后面的语句中都可以使用)
 avg,sum.... 
having 
select 
distinct 
order by
limit 

事务(Transaction)

是指作为单个逻辑工作单元的一系列操作,要么全部执行成功,要么全部失败,都不执行。

https://mariadb.com/kb/en/transactions/

四个特性

可参考此文章来记忆

  • 原子性:把多个操作看作一个原子,紧密联系不可分割
  • 隔离性:数据库为了保证性能,也支持高并发,但是有安全隐患。保证多个操作之间是隔离的,相互之间不影响
  • 持久性:对数据库的操作是持久生效的
  • 一致性:保证数据守恒,将 来广泛的应用到分布式系统里面。即事务发生前后业务上的总量保持一致。比如银行内部账户之间转账的事务发生前后,银行总的存量应该前后一致。
隔离级别
读未提交(Read uncommitted)

性能最好,但是安全性最差
个人理解,就是指一个事务在查询数据时,会受到其他尚未提交的事务已经“执行”的修改的影响。例如,事务A第一次查询后并未提交,但此时事务B开始了,然后执行了数据的插入,但是未提交,然后此时事务A又执行了一次查询,那么在当前级别下还没有被事务B提交的插入的数据,也会被查询出来。

读已提交(Read committed)

Oracle默认级别性稍好,安全性较好
个人理解,可以对比着读未提交来理解,就是只能查询到其他已经提交的数据,尚未提交的数据不会。

可重复读(repeatable read)

性能较差 ,但安全性好。
个人理解,事务开始后,查询到的数据会被直接生成一个快照(至于这个快照的粒度有多大就不清楚了,具说是MVCC),当前事务提交前再次执行同样的查询时,还是从这个快照上查询,因此也就实现了当前事务内,重复查询时数据一致。Mysql默认级别

串行化(Serializable)

个人理解:表级锁,一个表同一时间只能被一个事务所访问。效率低 下,安全性高,不能并发。

Mysql手动开启、提交和滚事务

默认情况,mysql每一条mysql语句都是一个单独的事务。如果需要在一个事务中包含多条SQL语句,那么需要手动开启事务和结束事务。

select @@tx_isolation; # 查询当前数据库的事务隔离级别
start transaction; --开启事务
# 这里放一堆需要同时执行成功的SQL语句
committed; --提交事务,把SQL语句对数据库的影响持久化。
rollback; --回滚事务,如果事务执行中途失败了,也会默认执行回滚,取消所有对数据的操作

insert into user(name,pwd) values ('Alice',md5('623')),('Qiura',md5('625')); ##批量插入时使用此种方式默认只会开启一次事务,可以提高效率

可以看林晓斌MySql实战45讲https://funnylog.gitee.io/mysql45/iframe/
如果打不开了可以看这个大搬运https://blog.csdn.net/qq_40378034/article/details/90904573
官方文档关于innodb的锁https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html

多表联查 join关键字

在这里插入图片描述

  • INNER JOIN两边都对应有记录的才展示,其他去掉
  • LEFT JOIN左边表中的数据都出现,右边没有数据以NULL填充
  • RIGHT JOIN右边表中的数据都出现,左边没有数据以NULL填充

子查询subquery

  • 概念:子查询是指嵌入在其他select中的select查询,也叫嵌套查询。即把内层的查询结果当作外层的查询条件来用
#查询“计算机导论”这门课程的总分数
#子查询的方式
select sum(degree) 
from scores 
where  cno = 
	(select cno 
	from courses 
	where cname = '计算机导论');
#联接的方式
select sum(degree) 
from scores as ta join courses as tb 
on ta.cno = tb.cno 
where tb.cname = '计算机导论';

#子查询只返回一个值时,可以直接使用 = 来做比较
select ename from emp where deptno = (select deptno from dept where dname = 'accounting');
#子查询返回的值超过一个时,请使用 in 关键字
select ename from emp where deptno in (select deptno from dept where loc = '二区');
  • 注意:并不是所有情况下都可以简单的由子查询方式转为联接查询
    当联接条件有一方是其所属表的主键时(能够确定其只会出现一条匹配到的记录),则一般是可以由子查询转为联接查询的。但如果两方都不是主键,且根据条件过滤后无法确保这两方中任意一方只剩下一条的情况下,建议使用子查询,因为联接查询会出现笛卡尔乘积的现象。当然了,此种情况下也可以通过distinct关键字来去掉重复项(在只显示需要处理的那个表的数据的前提下)。
  • 当子查询返回的结果超过一个值,且主查询的限定条件是某属性=子查询的结果时,会遇到如下错误
ERROR 1242 (21000): Subquery returns more than 1 row

索引

  • 定义:索引是一种排好序的支持快速查找数据结构,它帮助数据库高效的进行数据的检索。在数据之外,数据库系统还维护着满足特定查找算法的数据结构(额外的空间),这些数据结构以某种方式指向数据,这样就可以在这些数据结构上实现高效的查找算法,这种数据结构就叫做索引。

  • 分类:单值索引、唯一索引、复合索引

  • 单值索引:索引只包括一个列,表里面可以有其他的列

# 用来查看dept表中现有的索引有哪些,以下两条是一样的
show index from dept;
show keys from dept;
# 给dept表中的loc字段创建索引
create index loc_index on dept(loc);
# 查看所有表的索引
SELECT a.TABLE_SCHEMA,
a.TABLE_NAME,
a.index_name,
GROUP_CONCAT(column_name ORDER BY seq_in_index) AS `Columns`
FROM information_schema.statistics a
GROUP BY a.TABLE_SCHEMA,a.TABLE_NAME,a.index_name

索引查询结果列表中各字段的含义:
Non_unique:如果索引不能包括重复词,则为0。如果可以,则为1。
Key_name:索引的名称。
Seq_in_index:索引中的列序列号,从1开始。
Column_name:列名称。
Collation:列以什么方式存储在索引中。在MySQL中,有值‘A’(升序)或NULL(无分类)。
Cardinality:索引中唯一值的数目的估计值。通过运行ANALYZE TABLE或myisamchk -a可以更新。基数根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机 会就越大。
Sub_part:如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL。

Packed: 指示关键字如何被压缩。如果没有被压缩,则为NULL。
Null: 如果列含有NULL,则含有YES。如果没有,则该列含有NO。
Index_type:用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)。

  • 复合索引
    • 同时对多列创建索引
    • 如需使用索引,则条件字段必须包含索引最左侧的列,即创建索引时被放在第一位的列。否则将无法使用到此索引,条件字段的顺序无要求,只要包含即可。也称最左原则
# 创建复合索引
create index name_index on persons(LastName,FirstName);
select * from persons where LastName = 'lily';# 此查询会使用复合索引
select * from persons where FirstName = 'Mr';# 此查询不会使用复合索引
  • 唯一索引:索引列的值必须唯一,但允许有空值;主键会自动创建唯一索引

# 创建唯一索引
create unique index dname_unique_index on dept(dname);
# 删除索引
ALTER TABLE `dept` DROP INDEX `loc_index`;
  • 检查SQL的执行效率(观察有没有使用索引)使用EXPLAIN关键字
EXPLAIN SELECT * FROM emp WHERE ename = 'jack';

  • 特点
    • 索引是数据库优化的一个手段
    • 表的主键会默认自动创建索引
    • 每个字段都可以被索引
    • 大量降低数据库的IO磁盘读写成本,极大提高 了检索速度
    • 索引事先对数据进行了排序,大大提高了查询效率
  • 缺点
    • 索引本身也是一张表,该表保存了主键与索引字段,并指向实体记录,所以索引列也要占用空间
    • 索引表中的内容,在业务表中都有,数据是重复的,空间是“浪费的”
    • 虽然索引大大提高了查询的速度,但对数据的增、删、改的操作需要更新索引表信息,如果数据量非常巨大,更新效率就很慢,因为更新表时,Mysql不仅要保存数据,同时要更新索引。
    • 随着业务的不断变化 ,之前建立的索引可能不能满足查询需求,需要消耗我们的时间去更新索引。

视图

  • 概念
    可视化的表,视图当做是一个特殊的表,是指,把sql执行的结果,直接缓存到了视图中。
    下次还要发起相同的sql,直接查视图。现在用的少,了解即可.
  • 优点:提高了SQL的 复用性+屏蔽了业务表的复杂性+数据共享
  • 缺点:是一张单独的表存了业务表的数据造成了数据重复+无法优化。
    使用: 1,创建视图 2,使用视图
create view 视图名 as  SQL语句;
select * from 视图名;
#视图:就是一个特殊的表,缓存上次的查询结果
#好处是提高了SQL的复用率,坏处是占内存无法被优化
#1.创建视图
CREATE VIEW emp_view AS
SELECT * FROM emp WHERE ename LIKE '%a%' #模糊查询,名字里包含a的
#2.使用视图
SELECT * FROM emp_view
 

SQL优化

由索引牵出来的优化问题:https://blog.csdn.net/weixin_39796652/article/details/111668287

  1. 查询时尽量不要使用*,而是具体字段
  2. 避免在where子句中使用or来连接条件,减少查询结果的数据量
  3. 字段类型使用varchar 代替char
  4. 尽量使用数值代替字符串
主键(id):primary key优先使用数值类型inttinyint
性别(sex):0-代表女,1-代表男;数据库没有布尔类型,mysql推荐使用tinyint
支付方式(payment):1-现金、2-微信、3-支付宝、4-信用卡、5-银行卡
服务状态(state):1-开启、2-暂停、3-停止
商品状态(state):1-上架、2-下架、3-删除
  1. 查询尽量避免返回大量数据:会造成查询时间过长,网络传输时间过长。同时大量数据返回也没有实际意义。通常采用分页,一页习惯10、20、50、100
  2. 使用explain分析SQL执行计划
是否使用了索引及其扫描类型
typeALL 全表扫描,没有优化,最慢的方式
index 索引全扫描
range 索引范围扫描,常用语<<=>=between等操作
ref 使用非唯一索引扫描或唯一索引前缀扫描,返回单条记录,常出现在关联查询中
eq_ref 类似ref,区别在于使用的是唯一索引,使用主键的关联查询
const/system 单条记录,系统会把匹配行中的其他列作为常数处理,如主键或唯一索引查询
null MySQL不访问任何表或索引,直接返回结果
key:
真正使用的索引方式

Extra:原文连接

Extra说明解读
Using whereSQL使用了where条件过滤数据常见的优化方法为,在where过滤属性上添加索引。
Using indexSQL所需要返回的所有列数据均在一棵索引树上,而无需访问实际的行记录这类SQL语句往往性能较好
Using index condition确实命中了索引,但不是所有的列数据都在索引树上,还需要访问实际的行记录这类SQL语句性能也较高,但不如Using index
Using filesort得到所需结果集,需要对所有记录进行文件排序这类SQL语句性能极差,需要进行优化。  典型的,在一个没有建立索引的列上进行了order by,就会触发filesort,常见的优化方案是,在order by的列上添加索引,避免每次查询都全量排序
Using temporary需要建立临时表(temporary table)来暂存中间结果这类SQL语句性能较低,往往也需要进行优化。典型的,group by和order by同时存在,且作用于不同的字段时,就会建立临时表,以便计算出最终的结果集
Using join buffer (Block Nested Loop)需要进行嵌套循环计算这类SQL语句性能往往也较低,需要进行优化。典型的,两个关联表join,关联字段均未建立索引,就会出现这种情况。常见的优化方案是,在关联字段上添加索引,避免每次嵌套循环计算。
  1. 创建name字段,主要是因为使用这个name字段进行查询的频率大概率不会低
    alter table student add index inde_name(name); #也可以使用之前学的create index
  2. 优化like语句:当且仅当‘a_'或’a%'这种查以**开头的情况时会使用索引,否则无法使用索引
  3. 字符串怪现象:如果name字段是varchar类型并创建了索引,但是查询时传的是数字,则无法使用索引
  4. 索引不宜太多,一般5个以内
  • 虽提高查询效率,但降低插入和更新的速度
  • 索引本身也是表,也要占用空间
  1. 索引不适合创建在大量重复的字段上
  2. where限定条件尽量精确,避免返回不必要的数据,节省开销
  3. 避免在where中对字段进行计算
  4. 避免在where中使用!=<>,因为可能返回更多的数据
  5. 使用distinct去重的字段要尽量的少select distinct * from emp;# 这种就不建议
  6. 批量插入性能提升,大批量插入时可以以手工管理事务的方式
insert into studdent(id,name) values(4,'lily'),(5,'mary');# sqlyog工具导出后就是这样的写法,说是只开启一次事务
  1. 批量删除时尽量也使用分页的形式,比如通过限定id的范围
  2. 伪删除,设置商品状态(state):1-上架、2-下架、3-删除。这里的删除只是一个标记,并不直接从数据删除,可以作为历史记录使用
  3. 提高group by的效率,可以在执行到该语句前,先使用where把不需要的记录过滤掉
  4. 原则就是where和order by中常出现的字段就创建索引。
  5. 及时删除冗余和重复的索引
  6. in子查询的优化,

范式

用来设计表时的一些原则,范式NF(Normal Form),分为六大范式,通常只 需要了解三大范式就可以了

  • 第一范式:表里面的字段足够细化精确,不可再被分割
  • 第二 范式:基于第一范式的基础上产生的,指表里面都应该有主键,即用来唯一区分每一行的主关键字
  • 第三范式:基于前两个范式的基础上产生,要求一个数据库表中不包含已在其它表中已包含的非主关键字信息

JDBC

  • 定义JDBC(Java Database Connectivity):专门用来完成Java和数据库交互的技术。是一套接口,标准、规范。
  • 使用步骤:
  1. 导包:
    使用哪个版本 ,可以参考官网定义的mariadb对应的 Mysql的版本号

mysql-connector-java-5.1.32.jar #mariadb10.4(不含)之前的数据库的话用这个
mysql-connector-java-8.0.24.jar #mariadb10.4(含)开始的用这个

把上面的相应的jar包直接复制到project目录下,然后在IDEA里面,选中这个jar包,右键-Aad as Library,只要看到这个jar包前面有可以小箭头了,就说明导入成功了。
在这里插入图片描述

  1. 连接数据库:端口 库名 用户名 密码
  2. 写SQL,执行
  3. 处理数据返回给Java的结果
try {
    Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

try {
    //定义地址   协议:jdbc:mysql:  加mysql的地址
    String url = "jdbc:mysql://localhost/cgb2109";
    /*如果出现中文乱码,可采用如下的方式,增加编码指定*/
   	/*String url ="jdbc:mysql://localhost:3306/mydb?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false";*/
   	
    //获取连接
    Connection connection = DriverManager.getConnection(url, "root", "root");

    //获得statement
    Statement statement = connection.createStatement();

    //执行SQL并取得结果集
    ResultSet resultSet = statement.executeQuery("select * from dept;");

    //获取列名的集合
    ResultSetMetaData metaData = resultSet.getMetaData();

    //拿到列数
    int columnCount = metaData.getColumnCount();

    //遍历输出每一列的列名
    for (int i = 0; i < columnCount; i++) {
        System.out.print(metaData.getColumnName(i + 1) + "\t");
    }
    System.out.println();

    //遍历输出每一行,当resultSet还有下一行时,next()方法返回true
    while (resultSet.next()) {

        //遍历输出当前行的每一列的值
        for (int i = 0; i < columnCount; i++) {

            //当前全部统一使用getString()方法取出该列的值,还有getByte\getShort\getInt\getFloat\getDouble..
            System.out.print(resultSet.getString(i + 1) + "\t");
            int empno = resultSet.getInt("empno");
        }
        System.out.println();
    }

    //关闭连接
    connection.close();
} catch (SQLException throwables) {
    throwables.printStackTrace();
}

乱码问题

DriverManager.getConnection(url,userName,passWord)里面的url里面加入?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false来指定字符集

String url ="jdbc:mysql://localhost:3306/mydb?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false";

SQL注入

String condition = "陈强";
String condition = "陈强' or 1=1 or '";
String condition = "陈强' or true or '";

String sql = "select * from teachers where tname='" + condition+"'";

利用sql中’单撇是字符串的结束符,or只要一个条件成立其它就不用再判断,而恶意造成sql查询失效,本应该只展示一条数据,结果全部展现。

注入后形成的SQL:SELECT * FROM teachers WHERE tname='陈强' OR 1=1 OR ''

PreparedStatement 语句

try {
    Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
try {
    Connection connection = DriverManager.getConnection("jdbc:mysql:///cgb2109", "root", "root");
    PreparedStatement ps = connection.prepareStatement("select * from user where name = ? and pwd = ?");
    ps.setString(1, "a");
    ps.setInt(1, 123);
    ResultSet rs = ps.executeQuery();
    if (rs.next()) {
        System.out.println("登陆成功!");
    }
    ps.close();
    connection.close();
} catch (SQLException throwables) {
    throwables.printStackTrace();
}

常见错误

java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
错误原因:jar没有导入,没有builder pathClass.forName("com.mysql.jdbc.Driver"); 字符串拼写错误

Unknown database mydb;
错误原因:数据库名称拼写错误

Access denied for user ‘root123’@‘localhost’ (using password: YES)
错误原因:数据库用户名或者密码错误

Table ‘py-school-db.mydb’ doesn’t exist
错误原因:表不存在,也可能表名写错了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

水晶心泉

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

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

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

打赏作者

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

抵扣说明:

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

余额充值