0%

数据库

数据库

数据库的简介

数据库

数据库(database,DB)是指长期存储在计算机内的,有组织,可共享的数据的集合。数据库中的数据按一定的数学模型组织、描述和存储,具有较小的冗余,较高的数据独立性和易扩展性,并可为各种用户共享。

数据库管理系统软件

数据库管理系统(Database Management System)是一种操纵和管理数据库的大型软件,用于建立、使用和维护数据库,简称DBMS。它对数据库进行统一的管理和控制,以保证数据库的安全性和完整性。用户通过DBMS访问数据库中的数据,数据库管理员也通过dbms进行数据库的维护工作。它可使多个应用程序和用户用不同的方法在同时或不同时刻去建立,修改和询问数据库。大部分DBMS提供数据定义语言DDL(Data Definition Language)和数据操作语言DML(Data Manipulation Language),供用户定义数据库的模式结构与权限约束,实现对数据的追加、删除等操作。

数据库管理系统是数据库系统的核心,是管理数据库的软件。数据库管理系统就是实现把用户意义下抽象的逻辑数据处理,转换成为计算机中具体的物理数据处理的软件。有了数据库管理系统,用户就可以在抽象意义下处理数据,而不必顾及这些数据在计算机中的布局和物理位置。

常见的数据库管理软件:甲骨文的oracle,IBM的db2,sql server, Access,Mysql(开源,免费,跨平台).

数据库系统

数据库系统DBS(Data Base System,简称DBS)通常由软件、数据库和数据管理员组成。其软件主要包括操作系统、各种宿主语言、实用程序以及数据库管理系统。数据库由数据库管理系统统一管理,数据的插入、修改和检索均要通过数据库管理系统进行。数据管理员负责创建、监控和维护整个数据库,使数据能被任何有权使用的人有效使用。

img

MySQL

mysql的管理

安装

mac:

1
2
3
4
5
6
// 1.安装mysql
brew install mysql

// 2.运行
mysql.server start
mysql -uroot //默认root无密码,可以通过mysql_secure_installation设置

linux:

1
2
3
--yum -y install mariadb mariadb-server
OR
--yum -y install mysql mysql-server

Ubuntu:

1
sudo apt-get install mysql-server mysql-client

win:

1
--http://dev.mysql.com/downloads/mysql/ 
  1. 解压

  2. 放在环境目录下

  3. 配置环境变量

  4. 在解压目录新建配置文件my.ini

1
2
3
4
5
[mysqld]
basedir=D:\Environment\mysql-8.0.23\
datadir=D:\Environment\mysql-8.0.23\data\
port=3306
skip-grant-tables
  1. 管理员cmd,执行mysqld --install
  2. 初始化数据文件mysqld --initialize-insecure --user=mysql
  3. 启动mysql:net start mysql
  4. 进入管理界面:mysql -u root -p
  5. 修改密码alter user 'root'@'localhost' identified with mysql_native_password by '123456';
  6. 注释掉ini中的跳过密码
  7. 重启mysql。连接测试。

启动

1
2
3
4
5
--service mysqld start  #开启
--chkconfig mysqld on #设置开机自启
OR
--systemctl start mariadb
--systemctl enable mariadb

查看

1
2
-- ps aux |grep mysqld    #查看进程
-- netstat -an |grep 3306 #查看端口

设置密码

1
2
-- mysqladmin -uroot password '123'   #设置初始密码,初始密码为空因此-p选项没有用
-- mysqladmin -u root -p123 password '1234' #修改root用户密码

登录

1
2
3
-- mysql               #本地登录,默认用户root,空密码,用户为root@127.0.0.1
-- mysql -uroot -p1234 #本地登录,指定用户名和密码,用户为root@127.0.0.1
-- mysql -uroot -p1234 -h 192.168.31.95 #远程登录,用户为root@192.168.31.95

mysql的常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

-- 启动mysql服务与停止mysql服务命令:
net start mysql
net stop mysql

-- 登陆与退出命令:
mysql -h 服务器IP -P 端口号 -u 用户名 -p 密码 --prompt 命令提示符 --delimiter 指定分隔符
mysql -h 127.0.0.1 -P 3306 -uroot -p123
exit

-- 刷新权限
flush privileges;

-- my.ini文件:
[mysql]
default-character-set=gbk
[mysqld]
character-set-server=gbk

-- prompt 命令提示符(\D:当前日期 \d:当前数据库 \u:当前用户 \T开始日志 \t结束日志)

show warnings;
help();
select now();
select version();
select user;
delimiter 指定分隔符

忘记密码怎么办?

方法1:启动mysql时,跳过授权表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@controller ~]# service mysqld stop
[root@controller ~]# mysqld_safe --skip-grant-table &
[root@controller ~]# mysql
mysql> select user,host,password from mysql.user;
+----------+-----------------------+-------------------------------------------+
| user | host | password |
+----------+-----------------------+-------------------------------------------+
| root | localhost | *A4B6157319038724E3560894F7F932C8886EBFCF |
| root | localhost.localdomain | |
| root | 127.0.0.1 | |
| root | ::1 | |
| | localhost | |
| | localhost.localdomain | |
| root | % | *23AE809DDACAF96AF0FD78ED04B6A265E05AA257 |
+----------+-----------------------+-------------------------------------------+
mysql> update mysql.user set password=password("123") where user="root" and host="localhost";
mysql> flush privileges;
mysql> exit
[root@controller ~]# service mysqld restart
[root@controller ~]# mysql -uroot -p123

方法2(删库):

1
2
3
4
删除与权限相关的库mysql,所有的授权信息都丢失,主要用于测试数据库或者刚刚建库不久没有授权数据的情况(从删库到跑路)
[root@controller ~]# rm -rf /var/lib/mysql/mysql
[root@controller ~]# service mysqld restart
[root@controller ~]# mysql

sql及其规范

sql是Structured Query Language(结构化查询语言)的缩写。SQL是专为数据库而建立的操作命令集,是一种功能齐全的数据库语言。

在使用它时,只需要发出“做什么”的命令,“怎么做”是不用使用者考虑的。SQL功能强大、简单易学、使用方便,已经成为了数据库操作的基础,并且现在几乎所有的数据库均支持sql。

<1> 在数据库系统中,SQL语句不区分大小写(建议用大写) 。但字符串常量区分大小写。建议命令大写,表名库名小写;

表名称尽量使用`符号括起来

字符串用单引号括起来

<2> SQL语句可单行或多行书写,以“;”结尾关键词不能跨多行或简写

<3> 用空格和缩进来提高语句的可读性。子句通常位于独立行,便于编辑,提高可读性。

1
2
SELECT * FROM tb_table
WHERE NAME="YUAN";

<4> 注释:单行注释:–

​ 多行注释:/*……*/

<5>sql语句可以折行操作

<6> DDL,DML和DCL

1
2
3
4
5
6
7
8
9
10
--SQL中 DML、DDL、DCL区别 .

-- DML(data manipulation language):
它们是SELECT、UPDATE、INSERT、DELETE,就象它的名字一样,这4条命令是用来对数据库里的数据进行操作的语言

-- DDL(data definition language):
DDL比DML要多,主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表(TABLE) 的结构,数据类型,表之间的链接和约束等初始化工作上,他们大多在建立表时使用

-- DCL(Data Control Language):
是数据库控制功能。是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。在默认状态下,只有sysadmin,dbcreator,db_owner或db_securityadmin等人员才有权力执行DCL

数据库操作(DDL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 1.创建数据库(在磁盘上创建一个对应的文件夹)
create database [if not exists] db_name [character set xxx][collate utf8_general_ci]; character set gbk设置编码方式 collate设置字符串比较的方式

-- 2.查看数据库
show databases;查看所有数据库
show create database db_name; 查看数据库的创建方式
show warnings; 查看警告信息

-- 3.修改数据库
alter database db_name [character set xxx]

-- 4.删除数据库
drop database [if exists] db_name;

-- 5.使用数据库
切换数据库 use db_name; -- 注意:进入到某个数据库后没办法再退回之前状态,但可以通过use进行切换
查看当前使用的数据库 select database();

-- 6.显示表信息
describe db_name;

mysql数据类型

MySQL支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。

1
2
3
BIT 位类型
BOOLEAN --->自动转换为tinyint(1)
BOOL

数值类型

下面的表显示了需要的每个整数类型的存储和范围。

img

1
float(4,2) /*共4位,小数点后2位,99.99*/

日期和时间类型

表示时间值的日期和时间类型为DATETIME、DATE、TIMESTAMP、TIME和YEAR。

每个时间类型有一个有效值范围和一个”零”值,当指定不合法的MySQL不能表示的值时使用”零”值。

img

字符串类型

字符串类型指CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM和SET。该节描述了这些类型如何工作以及如何在查询中使用这些类型。

img

1
2
char(10) /*定长字符串,10字节*/
varchar(20) /*变长字符串,0~20字节*/

CHAR和VARCHAR类型类似,但它们保存和检索的方式不同。它们的最大长度和是否尾部空格被保留等方面也不同。在存储或检索过程中不进行大小写转换。

BINARY和VARBINARY类类似于CHAR和VARCHAR,不同的是它们包含二进制字符串而不要非二进制字符串。也就是说,它们包含字节字符串而不是字符字符串。

BLOB是一个二进制大对象,可以容纳可变数量的数据。有4种BLOB类型:TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB。它们只是可容纳值的最大长度不同。

有4种TEXT类型:TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT。这些对应4种BLOB类型,有相同的最大长度和存储需求。

数据表操作

每张表固定有几个字段:

1
2
3
4
5
id 主键
version 乐观锁
is_delete 伪删除
gmt_create 创建时间
gmt_update 修改时间

基础操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
-- 1.创建表(类似于一个excel表)

create table tab_name(
field1 type[完整性约束条件],
field2 type,
...
fieldn type
)[engine=][character set=xxx];
-- collate用于指明order的顺序

-- 创建一个员工表employee

create table employee(
id int primary key auto_increment ,
name varchar(20),
gender bit default 1, -- gender char(1) default 1 ----- 或者 TINYINT(1)
birthday date,
entry_date date,
job varchar(20),
salary double(4,2) unsigned,
resume text -- 注意,这里作为最后一个字段不加逗号
);


/* 约束:
primary key (非空且唯一) :能够唯一区分出当前记录的字段称为主键! 为索引
unique
not null
auto_increment 主键字段必须是数字类型。
外键约束 foreign key */

-- 2.查看表信息
desc tab_name 查看表结构
show columns from tab_name 查看表结构
show tables 查看当前数据库中的所有的表
show create table tab_name 查看当前数据库表建表语句

-- 3.修改表结构
-- (1)增加列(字段)
alter table tab_name add [column] 列名 类型[完整性约束条件][first|after 字段名];
alter table user add addr varchar(20) not null unique first/after username;
#添加多个字段
alter table users2
add addr varchar(20),
add age int first,
add birth varchar(20) after name;

-- (2)修改一列类型
alter table tab_name modify 列名 类型 [完整性约束条件][first|after 字段名];
alter table test_table modify test_col tinyint not null after id; #after调整顺序
alter table users2 modify age tinyint default 20;
alter table users2 modify age int after id;

-- (3)修改列名
alter table tab_name change [column] 列名 新列名 类型 [完整性约束条件][first|after 字段名];
alter table users2 change age Age int default 28 first;

-- (4)删除一列
alter table tab_name drop [column] 列名;
-- 思考:删除多列呢?删一个填一个呢?
alter table users2
add salary float(6,2) unsigned not null after name,
drop addr;

-- (5)修改表名
rename table 表名 to 新表名;

-- (6)修该表所用的字符集
alter table student character set utf8;

-- 4.删除表
drop table tab_name;

-- 5 添加主键,删除主键
alter table tab_name add primary key(字段名称,...)
alter table users drop primary key;

eg:
mysql> create table test5(num int auto_increment);
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
create table test(num int primary key auto_increment);
-- 思考,如何删除主键?
alter table test modify id int; -- auto_increment没了,但这样写主键依然存在,所以还要加上下面这句
alter table test drop primary key;-- 仅仅用这句也无法直接删除主键

-- 唯一索引
alter table tab_name add unique [index|key] [索引名称](字段名称,...)

alter table users add unique(name)-- 索引值默认为字段名show create table users;
alter table users add unique key user_name(name);-- 索引值为user_name

-- 添加联合索引
alter table users add unique index name_age(name,age);#show create table users;

-- 删除唯一索引
alter table tab_name drop {index|key} index_name

完整性约束条件之主键约束

单字段主键

主键字段特点:非空且唯一

1
2
3
4
5
create table users(
id INT primary key,
name varchar(20),
city varchar(20)
);

多字段联合主键

1
2
3
4
5
6
create table users2(
id INT,
name varchar(20),
city varchar(20),
primary key(name,id)
);

<1> 一张表只能有一个主键

<2> 主键类型不一定非是整型

表纪录操作

表纪录之增,删,改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
-- 1.增加一条记录insert

/*insert [into] tab_name (field1,filed2,.......) values (value1,value2,.......);*/


create table employee_new(
id int primary key auto_increment,
name varchar(20) not null unique,
birthday varchar(20),
salary float(7,2)
);

insert into employee_new (id,name,birthday,salary) values
(1,'yuan','1990-09-09',9000);

insert into employee_new values
(2,'alex','1989-08-08',3000);

insert into employee_new (name,salary) values
('xialv',1000);

-- 插入多条数据
insert into employee_new values
(4,'alvin1','1993-04-20',3000),
(5,'alvin2','1995-05-12',5000);

-- set插入: insert [into] tab_name set 字段名=值

insert into employee_new set id=12,name="alvin3";


-- 2.修改表记录 update tab_name set field1=value1,field2=value2,......[where 语句]

/* UPDATE语法可以用新值更新原有表行中的各列。
SET子句指示要修改哪些列和要给予哪些值。
WHERE子句指定应更新哪些行。如没有WHERE子句,则更新所有的行。*/


update employee_new set birthday="1989-10-24" WHERE id=1;

--- 将yuan的薪水在原有基础上增加1000元。
update employee_new set salary=salary+4000 where name='yuan';

-- 3.删除表纪录

delete from tab_name [where ....]

/* 如果不跟where语句则删除整张表中的数据
delete只能用来删除一行记录
delete语句只能删除表中的内容,不能删除表本身,想要删除表,用drop
TRUNCATE TABLE也可以删除表中的所有数据,词语句首先摧毁表,再新建表。此种方式删除的数据不能在事务中恢复。*/

105211
-- 删除表中名称为’alex’的记录。
delete from employee_new where name='alex';
-- 删除表中所有记录。(一条一条删除)
delete from employee_new;-- 注意auto_increment没有被重置:alter table employee auto_increment=1;
-- 使用truncate删除表中记录。(直接删除表)
truncate table emp_new;

思考:

<1> 存储时间用varchar可不可以呢?它与date数据类型又有什么区别呢?(只用来显示时,可以用varchar,如果要计算用date)

<2> 表中数据三条,id分别为1,2,3,突然插入一个id=7,那么下次作为主键的字增长的id会从几开始增长呢?(从7开始)

表纪录之查(单表查询)

运算符

运算符 语法 描述
is null a is null
is not null a is not null
between a between b and c
like a like b
in a in (a1,a2,a3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
-- 查询表达式

SELECT *|field1,filed2 ... FROM tab_name
WHERE 条件
GROUP BY field
HAVING 筛选
ORDER BY field
LIMIT 限制条数


---准备表

CREATE TABLE ExamResult(

id INT PRIMARY KEY auto_increment,
name VARCHAR (20),
JS DOUBLE ,
Django DOUBLE ,
OpenStack DOUBLE
);


INSERT INTO ExamResult VALUES (1,"yuan",98,98,98),
(2,"xialv",35,98,67),
(3,"alex",59,59,62),
(4,"wusir",88,89,82),
(5,"alvin",88,98,67),
(6,"yuan",86,100,55);


-- (1)select [distinct] *|field1,field2,...... from tab_name
-- 其中from指定从哪张表筛选,*表示查找所有列,也可以指定一个列
-- 表明确指定要查找的列,distinct用来剔除重复行。

-- 查询表中所有学生的信息。
select * from ExamResult;
-- 查询表中所有学生的姓名和对应的英语成绩。
select name,JS from ExamResult;
-- 过滤表中重复数据。
select distinct JS ,name from ExamResult;



-- (2)select 也可以使用表达式,并且可以使用: 字段 as 别名或者:字段 别名

-- 在所有学生分数上加10分特长分显示。

select name,JS+10,Django+10,OpenStack+10 from ExamResult;
-- 统计每个学生的总分。
select name,JS+Django+OpenStack from ExamResult;
-- 使用别名表示学生总分。
select name as 姓名,JS+Django+OpenStack as 总成绩 from ExamResult;
select name,JS+Django+OpenStack 总成绩 from ExamResult;

select name JS from ExamResult; -- what will happen?---->记得加逗号

-- (3)使用where子句,进行过滤查询。

-- 查询姓名为XXX的学生成绩
select * from ExamResult where name='yuan';
-- 查询英语成绩大于90分的同学
select id,name,JS from ExamResult where JS>90;
-- 查询总分大于200分的所有同学
select name,JS+Django+OpenStack as 总成绩 from
ExamResult where JS+Django+OpenStack>200 ;
-- where字句中可以使用:
-- 比较运算符:
> < >= <= <> !=
between 80 and 100 值在10到20之间
in(80,90,100) 值是10或20或30
like 'yuan%'
/*
pattern可以是%或者_,
如果是%则表示任意多字符,此例如唐僧,唐国强
如果是_则表示一个字符唐_,只有唐僧符合。两个_则表示两个字符:__
*/

-- 逻辑运算符
在多个条件直接可以使用逻辑运算符 and or not
-- 练习
-- 查询JS分数在 70-100之间的同学。
select name ,JS from ExamResult where JS between 80 and 100;
-- 查询Django分数为75,76,77的同学。
select name ,Django from ExamResult where Django in (75,98,77);
-- 查询所有姓王的学生成绩。
select * from ExamResult where name like '王%';
-- 查询JS分>90,Django分>90的同学。
select id,name from ExamResult where JS>90 and Django >90;
-- 查找缺考数学的学生的姓名
select name from ExamResult where Database is null;


-- (4)Order by 指定排序的列,排序的列即可是表中的列名,也可以是select 语句后指定的别名。

-- select *|field1,field2... from tab_name order by field [Asc|Desc]

-- Asc 升序、Desc 降序,其中asc为默认值 ORDER BY 子句应位于SELECT语句的结尾。
-- 练习:
-- 对JS成绩排序后输出。
select * from ExamResult order by JS;
-- 对总分排序按从高到低的顺序输出
select name ,(ifnull(JS,0)+ifnull(Django,0)+ifnull(Database,0))
总成绩 from ExamResult order by 总成绩 desc;
-- 对姓李的学生成绩排序输出
select name ,(ifnull(JS,0)+ifnull(Django,0)+ifnull(OpenStack,0))
总成绩 from ExamResult where name like 'a%'
order by 总成绩 desc;


-- (5)group by 分组查询:
CREATE TABLE order_menu(
id INT PRIMARY KEY auto_increment,
product_name VARCHAR (20),
price FLOAT(6,2),
born_date DATE,
class VARCHAR (20)
);


INSERT INTO order_menu (product_name,price,born_date,class) VALUES
("苹果",20,20170612,"水果"),
("香蕉",80,20170602,"水果"),
("水壶",120,20170612,"电器"),
("被罩",70,20170612,"床上用品"),
("音响",420,20170612,"电器"),
("床单",55,20170612,"床上用品"),
("草莓",34,20170612,"水果");


-- 注意,按分组条件分组后每一组只会显示第一条记录

-- group by字句,其后可以接多个列名,也可以跟having子句,对group by 的结果进行筛选。


-- 按位置字段筛选
select * from order_menu group by 5;

-- 练习:对购物表按类名分组后显示每一组商品的价格总和
select class,SUM(price)from order_menu group by class;

-- 练习:对购物表按类名分组后显示每一组商品价格总和超过150的商品
select class,SUM(price)from order_menu group by class
HAVING SUM(price)>150;



/*
having 和 where两者都可以对查询结果进行进一步的过滤,差别有:
<1>where语句只能用在分组之前的筛选,having可以用在分组之后的筛选;
<2>使用where语句的地方都可以用having进行替换
<3>having中可以用聚合函数,where中就不行。
*/


-- GROUP_CONCAT() 函数
SELECT id,GROUP_CONCAT(name),GROUP_CONCAT(JS) from ExamResult GROUP BY id;


-- (6)聚合函数: 先不要管聚合函数要干嘛,先把要求的内容查出来再包上聚合函数即可。
-- (一般和分组查询配合使用)

--<1> 统计表中所有记录

-- COUNT(列名):统计行的个数
-- 统计一个班级共有多少学生?先查出所有的学生,再用count包上
select count(*) from ExamResult;
-- 统计JS成绩大于70的学生有多少个?
select count(JS) from ExamResult where JS>70;
-- 统计总分大于280的人数有多少?
select count(name) from ExamResult
where (ifnull(JS,0)+ifnull(Django,0)+ifnull(OpenStack,0))>280;
-- 注意:count(*)统计所有行; count(字段)不统计null值.

-- SUM(列名):统计满足条件的行的内容和
-- 统计一个班级JS总成绩?先查出所有的JS成绩,再用sum包上
select JS as JS总成绩 from ExamResult;
select sum(JS) as JS总成绩 from ExamResult;
-- 统计一个班级各科分别的总成绩
select sum(JS) as JS总成绩,
sum(Django) as Django总成绩,
sum(OpenStack) as OpenStack from ExamResult;

-- 统计一个班级各科的成绩总和
select sum(ifnull(JS,0)+ifnull(Django,0)+ifnull(Database,0))
as 总成绩 from ExamResult;
-- 统计一个班级JS成绩平均分
select sum(JS)/count(*) from ExamResult ;
-- 注意:sum仅对数值起作用,否则会报错。

-- AVG(列名):
-- 求一个班级JS平均分?先查出所有的JS分,然后用avg包上。
select avg(ifnull(JS,0)) from ExamResult;
-- 求一个班级总分平均分
select avg((ifnull(JS,0)+ifnull(Django,0)+ifnull(Database,0)))
from ExamResult ;
-- Max、Min
-- 求班级最高分和最低分(数值范围在统计中特别有用)
select Max((ifnull(JS,0)+ifnull(Django,0)+ifnull(OpenStack,0)))
最高分 from ExamResult;
select Min((ifnull(JS,0)+ifnull(Django,0)+ifnull(OpenStack,0)))
最低分 from ExamResult;
-- 求购物表中单价最高的商品名称及价格
---SELECT id, MAX(price) FROM order_menu;--id和最高价商品是一个商品吗?

SELECT MAX(price) FROM order_menu;

-- 注意:null 和所有的数计算都是null,所以需要用ifnull将null转换为0!
-- -----ifnull(JS,0)


-- with rollup的使用

--<2> 统计分组后的组记录


-- (7) 重点:Select from where group by having order by
-- Mysql在执行sql语句时的执行顺序:
-- from where select group by having order by
-- 分析:
select JS as JS成绩 from ExamResult where JS成绩 >70; ---- 不成功
select JS as JS成绩 from ExamResult having JS成绩 >90; --- 成功

-- (8) limit
SELECT * from ExamResult limit 1; --显示第一条
SELECT * from ExamResult limit 2,5; --跳过前两条显示接下来的五条纪录
SELECT * from ExamResult limit 2,2;


-- (9) 使用正则表达式查询
SELECT * FROM employee WHERE emp_name REGEXP '^yu';

SELECT * FROM employee WHERE emp_name REGEXP 'yun$';

SELECT * FROM employee WHERE emp_name REGEXP 'm{2}';

外键约束

创建外键

外键一定要和关联主键的数据类型保持一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
方法1
create table test(
`id` int,
key `fk_id`(`id`),
constraint `fk_id` foreign key (`id`) references `student`(`id`)
)


方法2
--- 每一个班主任会对应多个学生 , 而每个学生只能对应一个班主任
----主表

CREATE TABLE ClassCharger(

id TINYINT PRIMARY KEY auto_increment,
name VARCHAR (20),
age INT ,
is_marriged boolean -- show create table ClassCharger: tinyint(1)

);

INSERT INTO ClassCharger (name,age,is_marriged) VALUES ("冰冰",12,0),
("丹丹",14,0),
("歪歪",22,0),
("姗姗",20,0),
("小雨",21,0);


----子表
CREATE TABLE Student(

id INT PRIMARY KEY auto_increment,
name VARCHAR (20),
charger_id TINYINT, --切记:作为外键一定要和关联主键的数据类型保持一致
-- [ADD CONSTRAINT charger_fk_student FOREIGN KEY (charger_id) REFERENCES ClassCharger(id)

) ENGINE=INNODB;

INSERT INTO Student(name,charger_id) VALUES ("alvin1",2),
("alvin2",4),
("alvin3",1),
("alvin4",3),
("alvin5",1),
("alvin6",3),
("alvin7",2);


DELETE FROM ClassCharger WHERE name="冰冰";
INSERT student (name,charger_id) VALUES ("yuan",1);
-- 删除居然成功,可是 alvin3显示还是有班主任id=1的冰冰的;

-----------增加外键和删除外键---------

ALTER TABLE student ADD CONSTRAINT abc
FOREIGN KEY(charger_id)
REFERENCES classcharger(id);


ALTER TABLE student DROP FOREIGN KEY abc;

INNODB支持的ON语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
--外键约束对子表的含义:   如果在父表中找不到候选键,则不允许在子表上进行insert/update

--外键约束对父表的含义: 在父表上进行update/delete以更新或删除在子表中有一条或多条对
-- 应匹配行的候选键时,父表的行为取决于:在定义子表的外键时指定的
-- on update/on delete子句


-----------------innodb支持的四种方式---------------------------------------

-----cascade方式 在父表上update/delete记录时,同步update/delete掉子表的匹配记录
-----外键的级联删除:如果父表中的记录被删除,则子表中对应的记录自动被删除--------

FOREIGN KEY (charger_id) REFERENCES ClassCharger(id)
ON DELETE CASCADE


------set null方式 在父表上update/delete记录时,将子表上匹配记录的列设为null
-- 要注意子表的外键列不能为not null

FOREIGN KEY (charger_id) REFERENCES ClassCharger(id)
ON DELETE SET NULL


------Restrict方式 :拒绝对父表进行删除更新操作(了解)

------No action方式 在mysql中同Restrict,如果子表中有匹配的记录,则不允许对父表对应候选键
-- 进行update/delete操作(了解)
MYISAM INNODB
事务支持 不支持 支持
数据行锁定 不支持 支持
外键约束 不支持 支持
全文索引 支持 支持
表空间大小 较小 较大,约为2倍

MYISAM节约空间,速度较快

INNODB安全性高,事物的处理,多表多用户操作

多表查询

准备表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
-- 准备两张表
-- company.employee
-- company.department

create table employee(
emp_id int auto_increment primary key not null,
emp_name varchar(50),
age int,
dept_id int
);

insert into employee(emp_name,age,dept_id) values
('A',19,200),
('B',26,201),
('C',30,201),
('D',24,202),
('E',20,200),
('F',38,204);


create table department(
dept_id int,
dept_name varchar(100)
);

insert into department values
(200,'人事部'),
(201,'技术部'),
(202,'销售部'),
(203,'财政部');

mysql> select * from employee;
+--------+----------+------+---------+
| emp_id | emp_name | age | dept_id |
+--------+----------+------+---------+
| 1 | A | 19 | 200 |
| 2 | B | 26 | 201 |
| 3 | C | 30 | 201 |
| 4 | D | 24 | 202 |
| 5 | E | 20 | 200 |
| 6 | F | 38 | 204 |
+--------+----------+------+---------+
6 rows in set (0.00 sec)

mysql> select * from department;
+---------+-----------+
| dept_id | dept_name |
+---------+-----------+
| 200 | 人事部 |
| 201 | 技术部 |
| 202 | 销售部 |
| 203 | 财政部 |
+---------+-----------+
4 rows in set (0.01 sec)

多表查询之连接查询

1.笛卡尔积查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
mysql> SELECT * FROM employee,department;

-- select employee.emp_id,employee.emp_name,employee.age,
-- department.dept_name from employee,department;

+--------+----------+------+---------+---------+-----------+
| emp_id | emp_name | age | dept_id | dept_id | dept_name |
+--------+----------+------+---------+---------+-----------+
| 1 | A | 19 | 200 | 200 | 人事部 |
| 1 | A | 19 | 200 | 201 | 技术部 |
| 1 | A | 19 | 200 | 202 | 销售部 |
| 1 | A | 19 | 200 | 203 | 财政部 |
| 2 | B | 26 | 201 | 200 | 人事部 |
| 2 | B | 26 | 201 | 201 | 技术部 |
| 2 | B | 26 | 201 | 202 | 销售部 |
| 2 | B | 26 | 201 | 203 | 财政部 |
| 3 | C | 30 | 201 | 200 | 人事部 |
| 3 | C | 30 | 201 | 201 | 技术部 |
| 3 | C | 30 | 201 | 202 | 销售部 |
| 3 | C | 30 | 201 | 203 | 财政部 |
| 4 | D | 24 | 202 | 200 | 人事部 |
| 4 | D | 24 | 202 | 201 | 技术部 |
| 4 | D | 24 | 202 | 202 | 销售部 |
| 4 | D | 24 | 202 | 203 | 财政部 |
| 5 | E | 20 | 200 | 200 | 人事部 |
| 5 | E | 20 | 200 | 201 | 技术部 |
| 5 | E | 20 | 200 | 202 | 销售部 |
| 5 | E | 20 | 200 | 203 | 财政部 |
| 6 | F | 38 | 204 | 200 | 人事部 |
| 6 | F | 38 | 204 | 201 | 技术部 |
| 6 | F | 38 | 204 | 202 | 销售部 |
| 6 | F | 38 | 204 | 203 | 财政部 |
+--------+----------+------+---------+---------+-----------+

2.内连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 查询两张表中都有的关联数据,相当于利用条件从笛卡尔积结果中筛选出了正确的结果。

select * from employee,department where employee.dept_id = department.dept_id;
--select * from employee inner join department on employee.dept_id = department.dept_id;

+--------+----------+------+---------+---------+-----------+
| emp_id | emp_name | age | dept_id | dept_id | dept_name |
+--------+----------+------+---------+---------+-----------+
| 1 | A | 19 | 200 | 200 | 人事部 |
| 2 | B | 26 | 201 | 201 | 技术部 |
| 3 | C | 30 | 201 | 201 | 技术部 |
| 4 | D | 24 | 202 | 202 | 销售部 |
| 5 | E | 20 | 200 | 200 | 人事部 |
+--------+----------+------+---------+---------+-----------+

3.外连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
--(1)左外连接:在内连接的基础上增加左边有右边没有的结果
left join可以连续使用
select * from employee left join department on employee.dept_id = department.dept_id;

+--------+----------+------+---------+---------+-----------+
| emp_id | emp_name | age | dept_id | dept_id | dept_name |
+--------+----------+------+---------+---------+-----------+
| 1 | A | 19 | 200 | 200 | 人事部 |
| 5 | E | 20 | 200 | 200 | 人事部 |
| 2 | B | 26 | 201 | 201 | 技术部 |
| 3 | C | 30 | 201 | 201 | 技术部 |
| 4 | D | 24 | 202 | 202 | 销售部 |
| 6 | F | 38 | 204 | NULL | NULL |
+--------+----------+------+---------+---------+-----------+

--(2)右外连接:在内连接的基础上增加右边有左边没有的结果

select * from employee RIGHT JOIN department on employee.dept_id = department.dept_id;

+--------+----------+------+---------+---------+-----------+
| emp_id | emp_name | age | dept_id | dept_id | dept_name |
+--------+----------+------+---------+---------+-----------+
| 1 | A | 19 | 200 | 200 | 人事部 |
| 2 | B | 26 | 201 | 201 | 技术部 |
| 3 | C | 30 | 201 | 201 | 技术部 |
| 4 | D | 24 | 202 | 202 | 销售部 |
| 5 | E | 20 | 200 | 200 | 人事部 |
| NULL | NULL | NULL | NULL | 203 | 财政部 |
+--------+----------+------+---------+---------+-----------+

--(3)全外连接:在内连接的基础上增加左边有右边没有的和右边有左边没有的结果

-- mysql不支持全外连接 full JOIN
-- mysql可以使用此种方式间接实现全外连接

select * from employee RIGHT JOIN department on employee.dept_id = department.dept_id
UNION
select * from employee LEFT JOIN department on employee.dept_id = department.dept_id;



+--------+----------+------+---------+---------+-----------+
| emp_id | emp_name | age | dept_id | dept_id | dept_name |
+--------+----------+------+---------+---------+-----------+
| 1 | A | 19 | 200 | 200 | 人事部 |
| 2 | B | 26 | 201 | 201 | 技术部 |
| 3 | C | 30 | 201 | 201 | 技术部 |
| 4 | D | 24 | 202 | 202 | 销售部 |
| 5 | E | 20 | 200 | 200 | 人事部 |
| NULL | NULL | NULL | NULL | 203 | 财政部 |
| 6 | F | 38 | 204 | NULL | NULL |
+--------+----------+------+---------+---------+-----------+

-- 注意 union与union all的区别:union会去掉相同的纪录

多表查询之复合条件连接查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 查询员工年龄大于等于25岁的部门

SELECT DISTINCT department.dept_name
FROM employee,department
WHERE employee.dept_id = department.dept_id
AND age>25;

105211

--以内连接的方式查询employee和department表,并且以age字段的升序方式显示

select employee.emp_id,employee.emp_name,employee.age,department.dept_name
from employee,department
where employee.dept_id = department.dept_id
order by age asc;

多表查询之子查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
-- 子查询是将一个查询语句嵌套在另一个查询语句中。
-- 内层查询语句的查询结果,可以为外层查询语句提供查询条件。
-- 子查询中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等关键字
-- 还可以包含比较运算符:= 、 !=、> 、<等


-- 1. 带IN关键字的子查询

---查询employee表,但dept_id必须在department表中出现过

select * from employee
where dept_id IN
(select dept_id from department);


+--------+----------+------+---------+
| emp_id | emp_name | age | dept_id |
+--------+----------+------+---------+
| 1 | A | 19 | 200 |
| 2 | B | 26 | 201 |
| 3 | C | 30 | 201 |
| 4 | D | 24 | 202 |
| 5 | E | 20 | 200 |
+--------+----------+------+---------+
5 rows in set (0.01 sec)



-- 2. 带比较运算符的子查询
-- =、!=、>、>=、<、<=、<>

-- 查询员工年龄大于等于25岁的部门
select dept_id,dept_name from department
where dept_id IN
(select DISTINCT dept_id from employee where age>=25);

-- 3. 带EXISTS关键字的子查询

-- EXISTS关字键字表示存在。在使用EXISTS关键字时,内层查询语句不返回查询的记录。
-- 而是返回一个真假值。Ture或False
-- 当返回Ture时,外层查询语句将进行查询;当返回值为False时,外层查询语句不进行查询

select * from employee
WHERE EXISTS
(SELECT dept_name from department where dept_id=203);

--department表中存在dept_id=203,Ture


select * from employee
WHERE EXISTS
(SELECT dept_name from department where dept_id=205);

-- Empty set (0.00 sec)


ps: create table t1(select * from t2);

补充:any/some/all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
【4】any/some/all
① any,in,some,all分别是子查询关键词之一

any 可以与=、>、>=、<、<=、<>结合起来使用,分别表示等于、大于、大于等于、小于、小于等于、不等于其中的任意一个数据。

all可以与=、>、>=、<、<=、<>结合是来使用,分别表示等于、大于、大于等于、小于、小于等于、不等于其中的其中的所有数据。

② any关键词可以理解为“对于子查询返回的列中的任一数值,如果比较结果为true,则返回true”。

例如:

select age from t_user where age > any (select age from t_user_copy);
假设表t_user 中有一行包含(10),t_user_copy包含(21,14,6),则表达式为true;如果t_user_copy包含(20,10),或者表t_user_copy为空表,则表达式为false。如果表t_user_copy包含(null,null,null),则表达式为unkonwn。

all的意思是“对于子查询返回的列中的所有值,如果比较结果为true,则返回true”

例如:

select age from t_user where age > all (select age from t_user_copy);

假设表t_user 中有一行包含(10)。如果表t_user_copy包含(-5,0,+5),则表达式为true,因为10比t_user_copy中的查出的所有三个值大。如果表t_user_copy包含(12,6,null,-100),则表达式为false,因为t_user_copy中有一个值12大于10。如果表t_user_copy包含(0,null,1),则表达式为unknown。如果t_user_copy为空表,则结果为true。

③ not in /in

not in 是 “<>all”的别名,用法相同。

语句in 与“=any”是相同的。

例如:

select s1 from t1 where s1 = any (select s1 from t2);
select s1 from t1 where s1 in (select s1 from t2);

语句some是any的别名,用法相同。

例如:

select s1 from t1 where s1 <> any (select s1 from t2);
select s1 from t1 where s1 <> some (select s1 from t2);

在上述查询中some理解上就容易了“表t1中有部分s1与t2表中的s1不相等”,这种语句用any理解就有错了。
---------------------
作者:流烟默
来源:CSDN
原文:https://blog.csdn.net/J080624/article/details/72910548
版权声明:本文为博主原创文章,转载请附上博文链接!

MySQL函数

官网:https://dev.mysql.com/doc/refman/8.0/en/sql-function-reference.html

常用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- 数学运算
select abs(-8);
select ceiling(3.2);
select floor(5.5);
select rand(); -- 0~1之间的随机数
select sign(10); -- 负数返回-1,正数返回1

-- 字符串函数
select char_length('asdf'); -- 字符串长度
select concat('asd','sa'); -- 拼接字符串(有长度限制)
select insert('我爱编程',1,3,'asdf'); -- 在指定位置插入字串(将第1到第3个字符替换为asdf)
select upper('a');
select lower('A');
select instr('asdf','a'); -- 返回第一次出现的子串的索引
select replace('abcd','ab','ww'); -- 替换
select substr("asdf",1,2);

-- 时间
select current_date(); -- 当前日期
select curdate(); -- 当前日期
select now(); -- 当前时间
select localtime(); -- 本地时间
select sysdate(); -- 系统时间
select year(now()); -- 年
select month(now()); -- 月

-- 系统
select system_user();
select version();
select user();

-- 加密
MD5('asdf')

MySQL事务

事务:要么都成功,要么都失败

ACID

  • Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
  • Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束触发器级联回滚等。
  • Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括未提交读(Read uncommitted)、提交读(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  • Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

隔离

1、脏读:读取了另一个事务未提交的数据。

2、不可重复读:在一个事务中读取表中的某一行数据,多次读取结果不同。

3、虚读(幻读):在事务内读取了别的事务插入的数据,导致前后读取不一致。

MySQL事务配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- mysql默认开启事务的自动提交。
set autocommit = 0; /*关闭*/
set autocommit = 1; /*开启*/

-- 手动处理事务
set autocommit = 0
-- 事务开始
start transaction -- 标记一个事务的开始,从这之后的sql都在同一个事务内。
...
...
-- 提交:持久化
commit
-- 回滚
rollback -- 事务一旦提交就被持久化了,无法回滚
-- 事务结束
set autocommit = 1

--
savepoint 保存点名 -- 设置一个事务的保存点
rollback to savepoint 保存点名 -- 回滚到保存点
release savepoint 保存点名 -- 撤销保存点

插入多行数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- 插入百万行数据
DELIMITER $$ -- 写函数之前必须要写

CREATE FUNCTION mock_data()
RETURN INT
BEGIN
DECLARE num INT DEFAULT 1000000;
DECLARE i INT DEFAULT 0;
WHILE i<num DO
INSERT INTO user(`name`,`email`) VALUES(CONCAT('用户',i),`asdflk@qq.com`);
SET i=i+1;
END WHILE;
RETURN i;
END;

SELECT mock_data();

MySQL 索引

MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度。

打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。

索引分单列索引和组合索引。单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引。组合索引,即一个索引包含多个列。

创建索引时,你需要确保该索引是应用在SQL 查询语句的条件(一般作为 WHERE 子句的条件)。

实际上,索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录。

上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点:虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。

建立索引会占用磁盘空间的索引文件。

索引背后的数据结构

http://blog.codinglabs.org/articles/theory-of-mysql-index.html

索引分类

  • 主键索引(PRIMARY KEY)
    • 唯一,非空
  • 唯一索引(UNIQUE KEY)
    • 可以重复
  • 常规索引(KEY/INDEX)
    • 默认的,index或key关键字来设置
  • 全文索引(FullText)
    • 特定的数据库引擎下
    • 快速定位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 索引的使用
-- 1.创建表的时候给字段增加索引
-- 2.创建完毕后,增加索引

-- 显示所有的索引信息
show index from student

-- 增加一个全文索引 索引名(列名)
alter table school.student add fulltext index `studentname`(`studentname`);

-- 分析mysql执行状态
EXPLAIN SELECT * FROM student; -- 非全文索引
EXPLAIN SELECT * FROM student WHERE MATCH(studentName) AGAINST('刘');

普通索引

创建索引

这是最基本的索引,它没有任何限制。它有以下几种创建方式:

1
CREATE INDEX indexName ON mytable(username(length)); 

如果是CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB和TEXT类型,必须指定 length。

修改表结构(添加索引)

1
ALTER table tableName ADD INDEX indexName(columnName)

创建表的时候直接指定

1
2
3
4
5
6
7
8
9
CREATE TABLE mytable(  

ID INT NOT NULL,

username VARCHAR(16) NOT NULL,

INDEX [indexName] (username(length))

);

删除索引的语法

1
DROP INDEX [indexName] ON mytable; 

唯一索引

它与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:

创建索引

1
CREATE UNIQUE INDEX indexName ON mytable(username(length)) 

修改表结构

1
ALTER table mytable ADD UNIQUE [indexName] (username(length))

创建表的时候直接指定

1
2
3
4
5
6
7
8
9
CREATE TABLE mytable(  

ID INT NOT NULL,

username VARCHAR(16) NOT NULL,

UNIQUE [indexName] (username(length))

);

其他索引

全文索引

1
2
3
4
5
6
7
create table emp(
id int,
name varchar(20),
salary double(7,2),
text varchar(100000),
FULLTEXT INDEX text_index (text)
)

多列索引

1
2
3
4
5
6
7
create table emp(
id int,
name varchar(20),
salary double(7,2),
text varchar(100000),
INDEX index_name_salary (name,salary)
)

使用ALTER 命令添加和删除索引

有四种方式来添加数据表的索引:

  • ALTER TABLE tbl_name ADD PRIMARY KEY (column_list):

    该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。

  • ALTER TABLE tbl_name ADD UNIQUE index_name (column_list): 这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。

  • ALTER TABLE tbl_name ADD INDEX index_name (column_list): 添加普通索引,索引值可出现多次。

  • **ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):**该语句指定了索引为 FULLTEXT ,用于全文索引。

以下实例为在表中添加索引。

1
mysql> ALTER TABLE testalter_tbl ADD INDEX (c);

你还可以在 ALTER 命令中使用 DROP 子句来删除索引。尝试以下实例删除索引:

1
mysql> ALTER TABLE testalter_tbl DROP INDEX c;

使用 ALTER 命令添加和删除主键

主键只能作用于一个列上,添加主键索引时,你需要确保该主键默认不为空(NOT NULL)。实例如下:

1
2
mysql> ALTER TABLE testalter_tbl MODIFY i INT NOT NULL;
mysql> ALTER TABLE testalter_tbl ADD PRIMARY KEY (i);

你也可以使用 ALTER 命令删除主键:

1
mysql> ALTER TABLE testalter_tbl DROP PRIMARY KEY;

删除主键时只需指定PRIMARY KEY,但在删除索引时,你必须知道索引名。


显示索引信息

你可以使用 SHOW INDEX 命令来列出表中的相关的索引信息。可以通过添加 \G 来格式化输出信息。

尝试以下实例:

1
2
mysql> SHOW INDEX FROM table_name; \G
........

练习:

https://www.cnblogs.com/yuanchenqi/articles/6424245.html

答案:https://www.cnblogs.com/wupeiqi/articles/5748496.html

查询每门课程前3名的几种方法https://blog.csdn.net/iteye_8331/article/details/81655117

CASE用法1

1
2
3
4
5
6
7
select A,B 
case
when case1 then result1
when case2 then result2
else result3
end
from

CASE用法2

1
2
3
4
5
case A
when case1 then result1
when case2 then result2
else result3
end
1
2
3
case when A>2 then result1
when A<2 then result2
end

注意:

group by sid 则结果中sid一定只会出现一次。

权限管理和备份

用户管理

实际为对mysql.user进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-- 创建用户
CREATE USER XX IDENTIFIED BY '123456'

-- 修改密码(修改当前用户密码)
SET PASSWORD = PASSWORD('11111') -- mysql8.0删除了password函数

-- 修改密码(修改指定用户的密码)
SET PASSWORD FOR XX = PASSWORD('123123')

-- 重命名
RENAME USER XX TO xxx

-- 用户授权(授权除了grant之外的权限)
GRANT ALL PRIVILEGES ON *.* TO xxx

-- 查询权限
SHOW GRANTS FOR xxx
SHOW GRANTS FOR root@localhost

-- root用户权限
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION

-- 撤销权限 REVOKE
REVOKE ALL PRIVILEGES ON *.* FROM xxx

-- 删除用户
drop user xxx

MySQL备份

  • 直接拷贝物理文件
  • 在可视化工具中导出
  • 命令行:mysqldump
1
2
3
4
5
6
7
8
# mysqldump -h 主机 -u用户 -p密码 库名 [表1 表2 表3]> 磁盘文件
mysqldump -hlocalhost -uroot -p12345 school studetn > D:/a.sql

# 导入
# 登录的情况下,切换到指定的数据库
source D:/a.sql

mysql -uroot -p123456 库名< D:/a.sql

规范数据库设计

设计方法

  • 分析需求
  • 概要设计:设计关系图E-R图

三大范式

第一范式:数据库表的每一列都是不可分割的原子数据

第二范式:在第一范式的基础上,非码属性必须完全依赖于候选码

第三范式:在第二范式的基础上,任何非主属性不依赖于其他非主属性。

规范数据库设计

  • 关联查询的表不得超过三张表
  • 考虑商业化的需求和目标,数据库的性能更加重要
  • 在规范性能的问题的时候,需要适当考虑规范性。
  • 故意给某些表增加一些冗余的字段。(从多表查询变成单表查询)
  • 故意增加一些计算列(从大数据量降为小数据量的查询)

JDBC

数据库驱动

应用程序操作数据库时需要使用数据库驱动进行。

SUN公司为了简化开发人员的操作,提供了一个Java操作数据库的规范,俗称JDBC。

1
2
3
4
5
6
op1=>operation: 应用
op2=>operation: JDBC
op3=>operation: 数据库驱动
op4=>operation: 数据库

op1->op2->op3->op4

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class demo1 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver"); // connector 8.0以上自动加载驱动

// 2.用户信息和url
/*
mysql:
jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3

oracle:
jdbc:oracle:thin:@localhost:1521:sid
*/
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String password = "123456";

// 3.连接成功,数据库对象
Connection connection = DriverManager.getConnection(url, username, password);

// 4.执行SQL的对象
Statement statement = connection.createStatement();

// 5.执行SQL的对象去执行SQL
String sql = "SELECT * FROM users";

ResultSet resultSet = statement.executeQuery(sql);
// 增、删、改使用statement.executeUpdate(sql);
while (resultSet.next()){
System.out.println("id="+resultSet.getObject("id"));
System.out.println("name="+resultSet.getObject("name"));
}

// 6.释放连接
resultSet.close();
statement.close();
connection.close();
}
}

mysql默认端口3306

oracle默认端口1521

statement对象

db.properties

1
2
3
4
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode&characterEncoding=utf8&useSSL=true
username=root
password=123456

JdbcUtils.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;

static {
try {
InputStream resourceAsStream = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");

// 加载驱动
Class.forName(driver);
} catch (Exception e) {
e.printStackTrace();
}
}

public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}

public static void releaseResource(Connection connection, Statement statement, ResultSet resultSet) {
releaseResultSet(resultSet);
releaseStatement(statement);
releaseConnection(connection);
}

public static void releaseResultSet(ResultSet resultSet){
if (resultSet != null) {
try {
resultSet.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}

public static void releaseStatement(Statement statement){
if (statement != null) {
try {
statement.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}

public static void releaseConnection(Connection connection){
if (connection != null) {
try {
connection.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}
}

testInsert.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class testInsert {
public static void main(String[] args) {
Connection conn = null;
Statement stat = null;
ResultSet res = null;
try {
conn = JdbcUtils.getConnection();
stat = conn.createStatement();
int i = stat.executeUpdate("INSERT INTO users(`id`,`NAME`,`PASSWORD`,email,birthday)\n" +
"VALUES (4,'小白','123456','1554asdf@qwek.com','2020-01-01')");
if (i>0) {
System.out.println("插入成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JdbcUtils.releaseResource(conn, stat, res);
}
}
}

PreparedStatement

SQL注入问题

在sql语句中添加非法的字符。

1
2
3
4
String sql = "select * from users where `name`='"+username+"' AND `password` = '"+password+"'";

如果用户输入的username为 ' or '1=1
在没有登录的情况下也能查询出结果。

preparedstatement

preparedstatement可以解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class testinsert2 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement stat = null;
ResultSet res = null;
try {
conn = JdbcUtils.getConnection();
stat = conn.prepareStatement("INSERT INTO users(`id`,`NAME`,`PASSWORD`,email,birthday)\n" +
"VALUES (4,'小白','123456','1554asdf@qwek.com','2020-01-01')");

stat.setInt(1,4); //参数的位置(从1开始),参数值
stat.setString(2,"xasdf");
stat.setString(3,"123");
stat.setString(4,"1239123@asdfk.com");
stat.setDate(5,new java.sql.Date(new java.util.Date().getTime()));

int i = stat.executeUpdate();
if (i>0) {
System.out.println("插入成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JdbcUtils.releaseResource(conn, stat, res);
}
}
}

数据库连接池

数据库连接—-执行完毕—释放,浪费资源

连接池:预留多个连接

最小连接数、最大连接数、连接超时

开源数据源实现

  • DBCP
  • C3P0
  • Druid:阿里巴巴

DBCP

maven:commons-dbcp commons-pool

dbcp.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456

#<!-- 初始化连接 -->
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:【属性名=property;】
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=UTF8

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED

JdbcUtils_DBCP.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class JdbcUtils_DBCP {
private static DataSource dataSource = null;
static {
try {
InputStream resourceAsStream = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);

//创建数据源 工厂模式
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}

public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}

public static void releaseResource(Connection connection, Statement statement, ResultSet resultSet) {
releaseResultSet(resultSet);
releaseStatement(statement);
releaseConnection(connection);
}

public static void releaseResultSet(ResultSet resultSet){
if (resultSet != null) {
try {
resultSet.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}

public static void releaseStatement(Statement statement){
if (statement != null) {
try {
statement.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}

public static void releaseConnection(Connection connection){
if (connection != null) {
try {
connection.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}
}

C3P0

maven:c3p0,mchange-commons-java

c3p0-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- 默认配置,只可以出现一次 -->
<default-config>
<!-- 连接超时设置30秒 -->
<property name="checkoutTimeout">3000</property>
<!-- 30秒检查一次connection的空闲 -->
<property name="idleConnectionTestPeriod">30</property>
<!--初始化的池大小 -->
<property name="initialPoolSize">2</property>
<!-- 最多的一个connection空闲时间 -->
<property name="maxIdleTime">30</property>
<!-- 最多可以有多少个连接connection -->
<property name="maxPoolSize">5</property>
<!-- 最少的池中有几个连接 -->
<property name="minPoolSize">2</property>
<!-- 批处理的语句
-->
<property name="maxStatements">50</property>
<!-- 每次增长几个连接 -->
<property name="acquireIncrement">3</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">
<![CDATA[jdbc:mysql://127.0.0.1:3306/bookstore?useUnicode=true&characterEncoding=UTF-8]]>
</property>
<property name="user">root</property>
<property name="password">root</property>
</default-config>
</c3p0-config>

JdbcUtils_C3P0.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class JdbcUtils_C3P0 {
private static ComboPooledDataSource dataSource = null;
static {
try {
// // 代码配置
// dataSource = new ComboPooledDataSource();
// dataSource.setDriverClass();
// dataSource.setUser();

// 文件配置
dataSource = new ComboPooledDataSource("MySQL");
} catch (Exception e) {
e.printStackTrace();
}
}
...
}