MySQL快速入门(二)

目录
  • MySQL快速入门(二)
  • 约束条件
    • 自增
    • 自增的特性
    • 主键
    • 外键
      • 级联更新/删除
      • 表与表之间的关系
      • 外键约束
  • 操作表方法
  • 查询关键字
    • 练习数据
    • select··from
    • where 筛选
    • group by 分组
      • 聚合函数
    • group_concat 和 concat函数
    • Having 过滤
    • Distinct 去重
    • ORDER BY 排序
      • 单列排序
      • 多列排序
    • 空值排序
    • limit 分页

MySQL快速入门(二)

image


  • 接上篇MySQL快速入门(一)

约束条件

在上一篇说到建表语句的完整结构

create table t(字段 类型 约束条件);

约束条件:
unsigned		让数字没有正负号
zerofill		多余的使用数字0填充
not null		非空
default			默认值
unique			唯一值
primary key		主键
foreign key		外键
auto_increment	 自增
# 无符号:id int unsigned
新增表数据的方式
		方式1:  按照字段顺序一一传值
			 insert into t1 values(1,'Hammer');
		方式2:  自定义传值顺序 甚至不传
			insert into t1(name,id) values('Hammer',1);
			insert into t1(id) values(1);  # name没有传就为null
	在MySQL中不传数据 会使用关键字NULL填充意思就是空 类似于python的None


# 非空
 create table t2(
    	id int,
        name varchar(32) not null
    );
    
# 默认值:所有的字段都可以设置默认值,用户不给该字段传值则使用默认的,否则使用传了的
create table t3(
		id int default 911,
		name varchar(16) default 'Hammer'
	);
	
# 唯一值:unique
单列唯一   ->    
create table t4(
			id int,
			name varchar(32) unique
		);
联合唯一
create table t5(
			id int,
			host varchar(32),
			port int,
			unique(host,port)
		);
		

自增

由于主键类似于数据的唯一标识,并且主键一般都是数字类型,一般将自增设置给序号类型的字段,比如id(主键),····

  • auto_increment
create table t6(
			id int primary key auto_increment,
			name varchar(32)
		);

自增的特性

通俗的理解,序号删除了就会接着以前的序号往下排,或者插入了序号失败的就不计入,那么在mysql中,序号插入失败后台也会记录,比如插入id=1失败了,那么下次就会从2开始记··

自增不会因为删除操作而回退
	delete from 无法影响自增
如果想要重置需需要使用truncate关键字
	truncate 表名  # 清空表数据并且重置主键值

主键

主键通俗的理解为是not null+unique就是主键,由此可见主键的特征就是非空且唯一,必须有唯一性,比如id,每个人的id号唯一,那么就可以给它设置一个主键作为唯一标识;

主键的存在也加快了数据的查询速度;

'''
InnoDB存储引擎规定了一张表必须有且只有一个主键,因为InnoDB是通过主键的方式来构造表的
'''
如果没有设置主键,InnoDB内部会如何变化?
情况一:没有主键和其他约束条件
# InnoDB会采用隐藏的字段作为主键 不能加快数据的查询

情况二:没有主键但是有非空且唯一的字段
# InnoDB会自动将该字段升级为主键
create table t7(
	id int not null unique,
    name varchar(32)
);

外键

外键的存在解决了表内容重点不清晰,重复字段浪费空间,扩展性差等问题;

比如一个员工表内有员工的基本信息,也统计了员工所在部门的相关信息,这样表的重点就会不清晰,到底是统计员工信息还是部门信息,并且一个部门会对应多个员工,会导致字段重复浪费空间等问题····

解决方式就是,员工表写员工信息,部门表写部门信息,多表存在,使用外键来使得表与表之间确立对应关系,员工所在的部门有该部门的编号,例如dep_id对应了部门表的id;从而确立了表与表之间的数据关系!

级联更新/删除

用外键做了表与表的对应关系后,那么操作(增删改)一个表数据,是不允许的;

那么可以通过添加级联更新和删除来同步数据,添加在设置外键下边···

  • on update cascade:级联更新
  • on delete cascade:级联删除

表与表之间的关系

  • 一对多
  • 多对多
  • 一对一
  • 没有对应关系

ps:表关系没有'多对一'

如何判断两个表或者多个表之间存在关系?用“换位思考”的方法

一对多

# 判断是否为一对多关系:
-> 员工表和部门表举例
--->员工表的角度:
		一个员工可以对应多个部门吗?
    	不可以!
--->部门表的角度:
		一个部门可以对应多个员工吗?
    	可以!
'''
类似上面的这种关系,一个可以一个不可以,那么表关系就是'一对多'!
'''

SQL语句实现

# 使用sql语句实现,先建立不含外键的表
create table dep(
	id int primary key auto_increment,
    dep_name varchar(32),
    dep_desc varchar(254)
);
create table emp(
	id int primary key auto_increment,
    name varchar(11),
    age int,
    dep_id int,
    foreign key(dep_id) references dep(id)
);

insert into dep(dep_name,dep_desc) values('财务部','财政大权'),('人事部','人才引进'),('销售部','商品出售');
mysql> select *from dep;
+----+-----------+--------------+
| id | dep_name  | dep_desc     |
+----+-----------+--------------+
|  1 | 财务部    | 财政大权     |
|  2 | 人事部    | 人才引进     |
|  3 | 销售部    | 商品出售     |
+----+-----------+--------------+
 insert into emp(name,age,dep_id) values('Hammer',18,1),('Hans',28,2),('jason',38,3),('he',18,1);
 mysql> select * from emp;
+----+--------+------+--------+
| id | name   | age  | dep_id |
+----+--------+------+--------+
|  1 | Hammer |   18 |      1 |
|  2 | Hans   |   28 |      2 |
|  3 | jason  |   38 |      3 |
|  4 | he     |   18 |      1 |
+----+--------+------+--------+
 # 这样插入表数据的时候,需要依据外键规定的部门编号来插入数据,部门编号是固定的,插入没有的部门编号报错
 mysql> insert into emp(name,age,dep_id) values('Hammer',18,5);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key 

多对多关系

# 同样使用换位思考的方法
书籍表和作者表举例
->书籍表的角度
---> 一本书可以对应多个作者吗?
	可以
---> 一个作者可以写多本书吗?
	可以
'''
类似这种双方都可以的情况,就是多对多的关系!
'''
# 注意!!!!
1、多对多关系创建表的时候,不能像"一对多"关系那样创建,因为两边有对应关系,需要都写入外键,那么创建一个表另外一个表没有创建,写入外键就会报错
2、此时,需要第三张表来存储对应关系

SQL语句实现

create table book(
	id int primary key auto_increment,
     book_name varchar(32),
     price float(6,2)
);
create author(
	id int primary key auto_increment,
    name varchar(11),
    age int
);
create table bookandauthor(
	id int primary key auto_increment,
    author_id int,
    book_id int,
    foreign key(author_id) references author(id)
    on update cascade  
    on delete cascade,
    foreign key(book_id) references book(id)
    on update cascade
    on delete cascade,
);

一对一关系

# 换位思考的方法
游戏人物表和任人物详细信息表
->游戏人物表角度
---> 一个人物可以对应多个详细信息吗?
     不可以
--->一个详细信息可以对应多个人物吗?
	不可以 
# 通俗理解为一个人一个身份证这种关系,就类似"9527"是你的编号一样~~~
'''
类似这种关系,双向不可以(×),就是一对一的关系或者没关系!
'''

注意

  • 外键字段建在任何一方都可以,但是推荐建在查询频率较高的表中

SQL语句实现

create table peo(
	id int primary key auto_increment,
    name varchar(11),
    height int,
    info_id int unique,
    foreign key(info_id) references det_info(id)
    on update cascade
    on delete cascade
);
create table det_info(
	id int primary key auto_increment,
    color varchar(32),
    skill varchar(32)
);

外键约束

  • 在创建表的时候,需要注意先创建没有设置外键的表
  • 在插入数据的时候,要确保被关联表中有数据
  • 在插入新数据的时候,外键字段只能填写被关联表中已经存在的数据
  • 在修改和删除被关联表中的数据的时候,无法直接操作,需要添加级联更新/删除

操作表方法

方法如下

方法名 说明 格式
rename 修改表名 ALTER TABLE 表名 RENAME 新表名;
add 增加字段(默认在尾部追加,可以通过first和after来指定字段添加的位置) ALTER TABLE 表名 ADD 字段名 数据类型 [完整性约束条件…],
ALTER TABLE 表名 ADD 字段名 数据类型 [完整性约束条件…] FIRST;
ALTER TABLE 表名 ADD 字段名 数据类型 [完整性约束条件…] AFTER 字段名;
drop 删除字段 ALTER TABLE 表名 DROP 字段名;
modify 修改字段 ALTER TABLE 表名 MODIFY 字段名 数据类型 [完整性约束条件…];
change 修改字段 ALTER TABLE 表名 CHANGE 旧字段名 新字段名 旧数据类型 [完整性约束条件…];

ps:modify只能改字段数据类型完整约束,不能改字段名,但是change可以!

查询关键字

练习数据

# 数据准备
create table emp(
  id int primary key auto_increment,
  name varchar(20) not null,
  sex enum('male','female') not null default 'male', #大部分是男的
  age int(3) unsigned not null default 28,
  hire_date date not null,
  post varchar(50),
  post_comment varchar(100),
  salary double(15,2),
  office int, #一个部门一个屋子
  depart_id int
);

#插入记录
#三个部门:教学,销售,运营
insert into emp(name,sex,age,hire_date,post,salary,office,depart_id) values
('Hammer','male',18,'20170301','CEO',7300.33,401,1), #以下是教学部
('tom','male',78,'20150302','teacher',1000000.31,401,1),
('kevin','male',81,'20130305','teacher',8300,401,1),
('tony','male',73,'20140701','teacher',3500,401,1),
('owen','male',28,'20121101','teacher',2100,401,1),
('jack','female',18,'20110211','teacher',9000,401,1),
('jenny','male',18,'19000301','teacher',30000,401,1),
('sank','male',48,'20101111','teacher',10000,401,1),
('哈哈','female',48,'20150311','sale',3000.13,402,2),#以下是销售部门
('呵呵','female',38,'20101101','sale',2000.35,402,2),
('西西','female',18,'20110312','sale',1000.37,402,2),
('乐乐','female',18,'20160513','sale',3000.29,402,2),
('拉拉','female',28,'20170127','sale',4000.33,402,2),
('僧龙','male',28,'20160311','operation',10000.13,403,3), #以下是运营部门
('程咬金','male',18,'19970312','operation',20000,403,3),
('程咬银','female',18,'20130311','operation',19000,403,3),
('程咬铜','male',18,'20150411','operation',18000,403,3),
('程咬铁','female',18,'20140512','operation',17000,403,3);

select··from

  • select * from 表名 :查询该所有数据
  • select 字段名1,字段2 from 表名:查询该表指定字段
select * from emp;
select id,name from emp;

where 筛选

where功能主要是针对查询出来的数据在分组前进行筛选

示例

1.查询id大于等于3小于等于6的数据
select * from emp where id>=3 and id<=6;
select * from emp where id between 3 and 6;
2.查询薪资是20000或者18000或者17000的数据
 select * from emp where salary in (20000,18000,17000);
 select * from emp where salary=18000 or salary=17000 or salary=20000;
3.查询员工姓名中包含o字母的员工姓名和薪资
 select name,salary from emp where name like '%o%';
4.查询员工姓名是由四个字符组成的员工姓名与其薪资
select name,salary from emp where name like '____';
select name salary from emp where char_length(name)=4;
5.查询id小于3或者大于6的数据
select * from emp where id<3 or id>6;
select * from emp where id not between 3 and 6;
6.查询薪资不在20000,18000,17000范围的数据
select * from emp where salary not in (20000,18000,17000);
7.查询岗位描述为空的员工名与岗位名 (针对null不能用等号,只能用is)
select name,post  from emp where post_comment is null;

group by 分组

分组是按照指定的条件,将个体归为一个个整体

需要注意的是,如果在不适用函数的情况下,按哪个字段分组, 那么查询的时候只能操作该字段相关的数据,比如按post分组,那么name,id等字段不可直接获取

针对5.6需要自己设置sql_mode
set global sql_mode = 'only_full_group_by,STRICT_TRANS_TABLES,PAD_CHAR_TO_FULL_LENGTH';
# 重新连接客户端

聚合函数

聚合函数主要就是配合分组一起使用

  • max:最大
  • min:最小
  • sum:总和
  • count:个数
  • avg:平均
1.按部门分组
select * from emp group by post;
# 分组后取出的是每个组的第一条数据
mysql> select id,name,sex from emp group by post;
+----+--------+--------+
| id | name   | sex    |
+----+--------+--------+
|  1 | Hammer | male   |
| 14 | 僧龙   | male   |
|  9 | 哈哈   | female |
|  2 | tom    | male   |
+----+--------+--------+

"""
设置sql_mode为only_full_group_by,意味着以后但凡分组,只能取到分组的依据,
不应该在去取组里面的单个元素的值,那样的话分组就没有意义了,因为不分组就是对单个元素信息的随意获取
"""

# 设置了之后再取数据,就得取和分组相关的数据,不然会报错
select * from emp group by post;  # 报错
select id,name,sex from emp group by post;  # 报错

# 只要分组了,就不能够再“直接”查找到单个数据信息了,只能获取到组名
select post from emp group by post;  # 获取部门信
+-----------+
| post      |
+-----------+
| CEO       |
| operation |
| sale      |
| teacher   |
+-----------+

分组只能获取组名,那么想获取到其它信息可以通过聚合函数来获取

1.获取每个部门的最高工资 
mysql> select post,max(salary) from emp group by post;
+-----------+-------------+
| post      | max(salary) |
+-----------+-------------+
| CEO       |     7300.33 |
| operation |    20000.00 |
| sale      |     4000.33 |
| teacher   |  1000000.31 |
+-----------+-------------+
mysql> select post as '部门',max(salary) as '工资' from emp group by post;
+-----------+------------+
| 部门      | 工资       |
+-----------+------------+
| CEO       |    7300.33 |
| operation |   20000.00 |
| sale      |    4000.33 |
| teacher   | 1000000.31 |
+-----------+------------+
# as 可以给查询结果起别名,来指定更明确的意思

2、每个部门的最低工资
mysql> select post as '部门',min(salary) as '工资' from emp group by post;
+-----------+----------+
| 部门      | 工资     |
+-----------+----------+
| CEO       |  7300.33 |
| operation | 10000.13 |
| sale      |  1000.37 |
| teacher   |  2100.00 |
+-----------+----------+
3、每个部门的平均工资
mysql> select post,avg(salary) from emp group by post;
+-----------+---------------+
| post      | avg(salary)   |
+-----------+---------------+
| CEO       |   7300.330000 |
| operation |  16800.026000 |
| sale      |   2600.294000 |
| teacher   | 151842.901429 |
+-----------+---------------+
4、每个部门的工资总和
mysql> select post,sum(salary) from emp group by post;
+-----------+-------------+
| post      | sum(salary) |
+-----------+-------------+
| CEO       |     7300.33 |
| operation |    84000.13 |
| sale      |    13001.47 |
| teacher   |  1062900.31 |
+-----------+-------------+
5、每个部门的人数
mysql> select post,count(id) from emp group by post;
+-----------+-----------+
| post      | count(id) |
+-----------+-----------+
| CEO       |         1 |
| operation |         5 |
| sale      |         5 |
| teacher   |         7 |
+-----------+-----------+

group_concat 和 concat函数

group_concat函数用于分组后,获取该字段的数据

mysql> select post,age,name from emp group by post; # 不使用group_concat函数报错
ERROR 1055 (42000): 'oldboy.emp.age' isn't in GROUP BY'

# 使用了group_concat函数
mysql> select post,group_concat(name,age) from emp group by post;
+-----------+----------------------------------------------------------+
| post      | group_concat(name,age)                                   |
+-----------+----------------------------------------------------------+
| CEO       | Hammer18                                                 |
| operation | 程咬铜18,程咬银18,程咬金18,僧龙28,程咬铁18               |
| sale      | 呵呵38,拉拉28,乐乐18,西西18,哈哈48                       |
| teacher   | jack18,sank48,owen28,tony73,kevin81,tom78,jenny18        |
+-----------+----------------------------------------------------------+


# group_concat函数还可以美化
mysql> select post,group_concat('{',name,':',age,'}') from emp group by post;
+-----------+-------------------------------------------------------------------------+
| post      | group_concat('{',name,':',age,'}')                                      |
+-----------+-------------------------------------------------------------------------+
| CEO       | {Hammer:18}                                                             |
| operation | {程咬铜:18},{程咬银:18},{程咬金:18},{僧龙:28},{程咬铁:18}               |
| sale      | {呵呵:38},{拉拉:28},{乐乐:18},{西西:18},{哈哈:48}                       |
| teacher   | {jack:18},{sank:48},{owen:28},{tony:73},{kevin:81},{tom:78},{jenny:18}  |
+-----------+-------------------------------------------------------------------------+

concat函数 不分组就可以使用

mysql> select concat('{',name,':',age,'}') from emp;

Having 过滤

where与having都是筛选功能区别在于:

where在分组前使用,having在分组后使用

# 获取部门所有年龄大于30平均薪资大于10000的员工名字
mysql> select post,group_concat(name) from emp where age>30 group by post  having avg(salary>30);
+---------+---------------------+
| post    | group_concat(name)  |
+---------+---------------------+
| sale    | 哈哈,呵呵           |
| teacher | tom,kevin,tony,sank |
+---------+---------------------+

Distinct 去重

去重操作,一定有重复数据,如果一列有重复数据,另一列没有,同时操作这两列也是不去重的

select distinct id,age from emp;  # NO
select distinct post from emp;   # YES

ORDER BY 排序

单列排序

按照单个字段或者表达式的值进行排序称为单列排序

SELECT col1, col2, ...
  FROM t
 ORDER BY col1 [ASC | DESC];
 
# ORDER BY 用于指定排序的字段;ASC升序排序,DESC 降序排序
# 获取部门编号为3的员工信息,并将工资从高到低排序
SELECT emp_name, salary
  FROM employee
 WHERE dept_id = 3
 ORDER BY salary DESC;
 
 # 统计各部门年龄在20岁以上的员工平均工资,并且保留平均工资大于1000的部门,然后对平均工资进行排序
select post,avg(salary) from emp where age>10 group by post having avg(salary)>1000 order by avg(salary) desc;

多列排序

多列排序是指基于多个字段或表达式的排序,使用逗号进行分隔

SELECT col1, col2, ...
  FROM t
 ORDER BY col1 ASC, col2 DESC, ...;
# 首先基于第一个字段进行排序;对于第一个字段排序相同的数据,再基于第二个字段进行排序;依此类推
# 获取部门编号为3的员工信息,并且按照月薪从高到低排序,月薪相同时再按照入职先后进行排序
SELECT emp_name, salary, hire_date
  FROM employee
 WHERE dept_id = 4
 ORDER BY salary DESC, hire_date;

空值排序

空值(NULL)在 SQL 中表示未知或者缺失的值,MySQL认为空值最小,升序时空值排在最前,降序时空值排在最后

insert into empinfo values(3,'hans','','2022-2-1');
select salary from empinfo order by salary;
+--------+
| salary |
+--------+
|        |
| 10000  |
| 20000  |
+--------+

limit 分页

MySQL支持使用 LIMIT 实现分页的效果

SELECT emp_name, salary
  FROM emp
 ORDER BY salary DESC
 LIMIT 5 OFFSET 0;
 
 # ORDER BY 按照月薪从高到低进行排序;OFFSET 表示跳过 0 行,LIMIT 返回前 5 条数据,也就是月薪 Top-5 的员工。另外,OFFSET 可以省略,

MySQL支持两种写法:

  • LIMIT 5 OFFSET 10
  • LIMIT 10, 5
  • 都表示跳过 10 行,返回随后的 5 行数据

你可能感兴趣的:(MySQL快速入门(二))