MySQL表的完整性约束及各子句查询操作

文章目录

  • MySQL表的完整性约束
    • 一、非外键约束
    • 二、外键约束
  • DQL查询操作
    • 一、单表查询
      • 1、简单查询:
      • 2、where子句
      • 3、使用函数进行查询
      • 4、group by分组
      • 5、having分组后筛选
      • 单表查询小总结
    • 二、多表查询
      • 1、交叉连接、自然连接、内连接
      • 2、外连接
    • 三、子查询
      • 1、不相关子查询
      • 2、相关子查询


MySQL表的完整性约束

为了防止数据库中存入不符合规范的数据,在用户进行插入、修改、删除数据等操作时,MySQL提供了完整性约束机制来对数据库中的数据进行检查,看其是否满足规定条件,以满足数据库中数据的准确性和一致性。
MySQL主要支持的几种完整性约束如下表所示:

约束条件 约束机制
PRIMARY KEY 主键约束,约束字段的值可唯一地标识对应的记录
NOT NULL 非空约束,约束字段的值不能为空
UNIQUE 唯一约束,约束字段的值是唯一的
CHECK 检查约束,限制某个字段的取值范围
DEFAULT 默认值约束,约束字段的默认值
AUTO_INCREMENT 自动增加约束,约束字段的值自动递增
FOREIGN KEY 外键约束,约束表与表之间的关系

上表中7中约束被分为两类,外键约束为一类,其他约束为一类,即非外键约束。

一、非外键约束

1、主键约束
主键约束(PRIMARY KEY,缩写PK)是数据库中最重要的一种约束,其作用是约束表中的某个字段可以唯一标识一条记录。因此,使用主键约束可以快速查找表中的记录,就像人的身份证、学生的学号一样,设置为主键的字段取值不能重复(唯一),也不能为空(非空),否则无法唯一识别一条记录。
主键可以是单个字段,也可以是多个字段组合。对于单字段主键的添加可使用表级约束,也可以使用列级约束;而对于多字段主键的添加只能使用表级约束。
2、非空约束
非空约束(NOT NULL,缩写NK)规定了一张表中指定的某个字段的值不能为空(NULL),设置了非空约束的字段在插入的数据为NULL时,数据库会提示错误,导致数据无法插入。无论是单个字段还是多个字段非空约束的添加只能使用列级约束(非空约束无表级约束)。
3、唯一约束
唯一约束(UNIQUE,缩写UK)它规定了一张表中指定的某个字段的值不能重复,即这一字段的每个值都是唯一的。如果想要某个字段的值不重复,那么就可以为该字段添加为唯一约束。
无论单个字段还是多个字段唯一约束的添加均可使用列级约束和表级约束
4、检查约束
检查约束(CHECK)用来限制某个字段的取值范围,可以定义为列级约束,也可以定义为表级约束。MySQL8开始支持检查约束。
5、默认值约束
默认值约束(DEFAULT)用来规定字段的默认值。如果某个被设置为DEFAULT约束的字段没插入具体值,那么该字段的值将会被默认值填充。
默认值约束的设置与非空约束一样,也只能使用列级约束。
6、自增约束
自增约束(AUTO_INCREMENT)可以使表中某个字段的值自动增加。一张表中只能有一个自增长字段,并且该字段必须定义了约束(该约束可以是主键约束、唯一约束以及外键约束),如果自增字段没有定义约束,数据库则会提示“Incorrect table definition; there can be only one auto column and it must be defined as a key”错误。
由于自增约束会自动生成唯一的ID,所以自增约束通常会配合主键使用,并且只适用于整数类型。一般情况下,设置为自增约束字段的值会从1开始,每增加一条记录,该字段的值加1。
现在用代码来演示非外键约束:

/*
建立一张用来存储学生信息的表
字段包含学号、姓名、性别,年龄、入学日期、班级,email等信息
约束:
建立一张用来存储学生信息的表
字段包含学号、姓名、性别,年龄、入学日期、班级,email等信息
【1】学号是主键 = 不能为空 +  唯一 ,主键的作用:可以通过主键查到唯一的一条记录
【2】如果主键是整数类型,那么需要自增
【3】姓名不能为空
【4】Email唯一
【5】性别默认值是男
【6】性别只能是男女
【7】年龄只能在18-50之间
*/
-- 创建数据库表:
create table t_student(
        sno int(6) primary key auto_increment, 
        sname varchar(5) not null, 
        sex char(1) default '男' check(sex='男' || sex='女'),
        age int(3) check(age>=18 and age<=50),
        enterdate date,
        classname varchar(10),
        email varchar(15) unique
);
-- 添加数据:
insert into t_student values (1,'张三','男',21,'2023-9-1','java01班','[email protected]');
insert into t_student values (2,'李四','男',21,'2023-9-1','java01班','[email protected]');
-- > 1062 - Duplicate entry '[email protected]' for key 't_student.email' 违反唯一约束
insert into t_student values (3,'露露','男',21,'2023-9-1','java01班','[email protected]');
insert into t_student (sname,enterdate) values ('菲菲','2029-4-5');
insert into t_student values (null,'小明','男',21,'2023-9-1','java01班','[email protected]');
insert into t_student values (default,'小刚','男',21,'2023-9-1','java01班','[email protected]');
-- 查看数据:
select * from t_student;

约束从作用上可以分为两类:
(1)表级约束:可以约束表中任意一个或多个字段。与列定义相互独立,不包含在列定义中;与定义用‘,’分隔;必须指出要约束的列的名称;
(2)列级约束:包含在列定义中,直接跟在该列的其它定义之后 ,用空格分隔;不必指定列名;

-- 删除表:
drop table t_student;
-- 创建数据库表:
create table t_student(
        sno int(6) auto_increment, 
        sname varchar(5) not null, 
        sex char(1) default '男',
        age int(3),
        enterdate date,
        classname varchar(10),
        email varchar(15),
        constraint pk_stu primary key (sno),  -- pk_stu 主键约束的名字
        constraint ck_stu_sex check (sex = '男' || sex = '女'),
        constraint ck_stu_age check (age >= 18 and age <= 50),
        constraint uq_stu_email unique (email)
);
-- 添加数据:
insert into t_student values (1,'张三','男',21,'2023-9-1','java01班','[email protected]');
-- 查看数据:
select * from t_student;

在创建表以后添加约束:

-- 删除表:
drop table t_student;
-- 创建数据库表:
create table t_student(
        sno int(6), 
        sname varchar(5) not null, 
        sex char(1) default '男',
        age int(3),
        enterdate date,
        classname varchar(10),
        email varchar(15)
);
-- 在创建表以后添加约束:
alter table t_student add constraint pk_stu primary key (sno) ; -- 主键约束
alter table t_student modify sno int(6) auto_increment; -- 修改自增条件
alter table t_student add constraint ck_stu_sex check (sex = '男' || sex = '女');
alter table t_student add constraint ck_stu_age check (age >= 18 and age <= 50);
alter table t_student add constraint uq_stu_email unique (email);
-- 查看表结构:
desc t_student;

验证约束添加成功:查看表结构
MySQL表的完整性约束及各子句查询操作_第1张图片

二、外键约束

外键约束(FOREIGN KEY,缩写FK)是用来实现数据库表的参照完整性的。外键约束可以使两张表紧密的结合起来,特别是针对修改或者删除的级联操作时,会保证数据的完整性。
外键是指表中某个字段的值依赖于另一张表中某个字段的值,而被依赖的字段必须具有主键约束或者唯一约束。被依赖的表我们通常称之为父表或者主表,设置外键约束的表称为子表或者从表。

举个例子:如果想要表示学生和班级的关系,首先要有学生表和班级表两张表,然后学生表中有个字段为stu_clazz(该字段表示学生所在的班级),而该字段的取值范围由班级表中的主键cla_no字段(该字段表示班级编号)的取值决定。那么班级表为主表,学生表为从表,且stu_clazz字段是学生表的外键。通过stu_clazz字段就建立了学生表和班级表的关系。
MySQL表的完整性约束及各子句查询操作_第2张图片
主表(父表):班级表 - 班级编号 - 主键
从表(子表):学生表 - 班级编号 - 外键

-- 先创建父表:班级表:
create table t_class(
        cno int(4) primary key auto_increment,
        cname varchar(10) not null,
        room char(4)
)
-- 添加班级数据:
insert into t_class values (null,'java001','r803');
insert into t_class values (null,'java002','r416');
insert into t_class values (null,'大数据001','r103');
-- 可以一次性添加多条记录:
insert into t_class values (null,'java001','r803'),(null,'java002','r416'),(null,'大数据001','r103');
-- 查询班级表:
select * from t_class;
-- 学生表删除:
drop table t_student;
-- 创建子表,学生表:
create table t_student(
        sno int(6) primary key auto_increment, 
        sname varchar(5) not null, 
        classno int(4)  -- 取值参考t_class表中的cno字段,不要求字段名字完全重复,但是类型长度定义 尽量要求相同。
);
-- 添加学生信息:
insert into t_student values (null,'张三',1),(null,'李四',1),(null,'王五',2);
-- 查看学生表:
select * from t_student;
-- 出现问题:
-- 1.添加一个学生对应的班级编码为4:
insert into t_student values (null,'丽丽',4);
-- 2.删除班级2:
delete from t_class where cno = 2;
-- 出现问题的原因:
-- 因为现在的外键约束,没用语法添加进去,现在只是逻辑上认为班级编号是外键,没有从语法上定义
-- 解决办法,添加外键约束:
-- 注意:外键约束只有表级约束,没有列级约束:
create table t_student(
        sno int(6) primary key auto_increment, 
        sname varchar(5) not null, 
        classno int(4),-- 取值参考t_class表中的cno字段,不要求字段名字完全重复,但是类型长度定义 尽量要求相同。
                                constraint fk_stu_classno foreign key (classno) references t_class (cno)
);
create table t_student(
        sno int(6) primary key auto_increment, 
        sname varchar(5) not null, 
        classno int(4)
);
-- 在创建表以后添加外键约束:
alter table t_student add constraint fk_stu_classno foreign key (classno) references t_class (cno)
-- 上面的两个问题都解决了:
-- 添加学生信息:
-- > 1452 - Cannot add or update a child row: a foreign key constraint fails (`mytestdb`.`t_student`, CONSTRAINT `fk_stu_classno` FOREIGN KEY (`classno`) REFERENCES `t_class` (`cno`))
insert into t_student values (null,'张三',1),(null,'李四',1),(null,'王五',2);
-- 删除班级1:
-- 2.删除班级2:
insert into t_student values (null,'张三',3),(null,'李四',3),(null,'王五',3);
-- > 1451 - Cannot delete or update a parent row: a foreign key constraint fails (`mytestdb`.`t_student`, CONSTRAINT `fk_stu_classno` FOREIGN KEY (`classno`) REFERENCES `t_class` (`cno`))
delete from t_class where cno = 3;

DQL查询操作

首先准备好四张表如下,本章节所有查询都是基于这四张表来操作。
下载Navicat,本章所有语句均可单步执行。在Navicat里新建查询,选中某句或多句sql语句,运行已选择的语句,即可查看到执行结果。

-- 准备四张表:dept(部门表),emp(员工表),salgrade(薪资等级表),bonus(奖金表)

create table DEPT(  
  DEPTNO int(2) not null,  
  DNAME  VARCHAR(14),  
  LOC    VARCHAR(13)  
);  
alter table DEPT  
  add constraint PK_DEPT primary key (DEPTNO); 
        
create table EMP  
(  
  EMPNO    int(4) primary key,  
  ENAME    VARCHAR(10),  
  JOB      VARCHAR(9),  
  MGR      int(4),  
  HIREDATE DATE,  
  SAL      double(7,2),  
  COMM     double(7,2),  
  DEPTNO   int(2)  
);  
alter table EMP  
  add constraint FK_DEPTNO foreign key (DEPTNO)  
  references DEPT (DEPTNO);  
        
create table SALGRADE  
(  
  GRADE int primary key,  
  LOSAL double(7,2),  
  HISAL double(7,2)  
);  
create table BONUS  
(  
  ENAME VARCHAR(10),  
  JOB   VARCHAR(9),  
  SAL   double(7,2),  
  COMM  double(7,2)  
);  
insert into DEPT (DEPTNO, DNAME, LOC)  
values (10, 'ACCOUNTING', 'NEW YORK');  
insert into DEPT (DEPTNO, DNAME, LOC)  
values (20, 'RESEARCH', 'DALLAS');  
insert into DEPT (DEPTNO, DNAME, LOC)  
values (30, 'SALES', 'CHICAGO');  
insert into DEPT (DEPTNO, DNAME, LOC)  
values (40, 'OPERATIONS', 'BOSTON');  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800, null, 20);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600, 300, 30);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250, 500, 30);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975, null, 20);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250, 1400, 30);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850, null, 30);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450, null, 10);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3000, null, 20);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7839, 'KING', 'PRESIDENT', null, '1981-11-17', 5000, null, 10);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 1500, 0, 30);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100, null, 20);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950, null, 30);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000, null, 20);  
insert into EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)  
values (7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300, null, 10);  
insert into SALGRADE (GRADE, LOSAL, HISAL)  
values (1, 700, 1200);  
insert into SALGRADE (GRADE, LOSAL, HISAL)  
values (2, 1201, 1400);  
insert into SALGRADE (GRADE, LOSAL, HISAL)  
values (3, 1401, 2000);  
insert into SALGRADE (GRADE, LOSAL, HISAL)  
values (4, 2001, 3000);  
insert into SALGRADE (GRADE, LOSAL, HISAL)  
values (5, 3001, 9999);  
-- 查看表:
select * from dept; 
-- 部门表:dept:department 部分 ,loc - location 位置
select * from emp;
-- 员工表:emp:employee 员工   ,mgr :manager上级领导编号,hiredate 入职日期  firedate 解雇日期 ,common:补助
-- deptno 外键 参考  dept - deptno字段
-- mgr 外键  参考  自身表emp - empno  产生了自关联
select * from salgrade;
-- losal - lowsal
-- hisal - highsal
select * from bonus;

一、单表查询

1、简单查询:

-- 对emp表查询:
select * from emp;
-- 显示部分列:
select EMPNO,ENAME,JOB,SAL from emp;
-- 显示部分行:
select * from emp where empno = 7521;
-- 起别名:
select empno as empno111,ename,job,sal  from emp where empno = 7521;
select empno 员工编号,ename 员工姓名,job 职位,sal 薪水 from emp where empno = 7521;
select empno as '员工编号',ename as 员工姓名,job as "职位" from emp where empno = 7521;
-- select empno as '员工编号',ename as 员工 姓名,sal as "薪水" from emp where empno = 7521;
-- 上面一行会报错(报错信息1064,如下),因为当别名中有特殊字符比如空格时,''或""不能省略
-- 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '姓名,sal as "薪水" from emp where empno = 7521' at line 1

-- 算术运算符:
select empno 员工编号,ename 员工姓名,job 职位,sal 薪水,sal+1000,sal-1000 from emp where empno = 7521;
select empno,ename,sal,comm,sal+comm from emp;  -- ???后面再说  此句运行后comm列值为Null的sal+comm列值也为Null,而不是想象中将comm的值当成0得出sal+comm的加和结果

-- 去重操作:
select job from emp;-- 14条记录,所有人的job都列出来了,无论重复与否
select distinct job from emp;-- 5条记录
select job,sal from emp;
select distinct job,sal from emp; -- 对后面的所有列组合 去重 ,而不是单独的某一列去重

-- 排序:
select job,sal from emp order by sal;
select job,sal from emp order by sal asc;
select * from emp order by sal desc;
select * from emp order by sal asc,deptno desc;

2、where子句

指定查询条件使用where子句,可以查询符合条件的部分记录。

-- 查看emp表:
select * from emp;
-- where子句:将过滤条件放在where子句的后面,可以筛选/过滤出我们想要的符合条件的数据:
-- where 子句 + 关系运算符
select * from emp where deptno = 10;
select * from emp where deptno > 10;
select * from emp where deptno >= 10;
select * from emp where deptno < 10;
select * from emp where deptno <= 10;
select * from emp where deptno <> 10;
select * from emp where deptno != 10;
select * from emp where job = 'CLERK'; 
select * from emp where job = 'clerk'; -- 默认情况下不区分大小写 
select * from emp where binary job = 'clerk'; -- binary区分大小写
select * from emp where hiredate < '1981-12-25';
-- where 子句 + 逻辑运算符:and 
select * from emp where sal > 1500 and sal < 3000;  -- (1500,3000)
select * from emp where sal > 1500 && sal < 3000; 
select * from emp where sal > 1500 and sal < 3000 order by sal;
select * from emp where sal between 1500 and 3000; -- [1500,3000]
-- where 子句 + 逻辑运算符:or
select * from emp where deptno = 10 or deptno = 20;
select * from emp where deptno = 10 || deptno = 20;
select * from emp where deptno in (10,20);
select * from emp where job in ('MANAGER','CLERK','ANALYST');

-- where子句 + 模糊查询:
-- 查询名字中带A的员工  -- %代表任意多个字符 0,1,2,.....
select * from emp where ename like '%A%';
select * from t_student where sname like '%三%';
-- 任意一个字符
select * from emp where ename like '__A%';
-- 关于null的判断:
select * from emp where comm is null;
select * from emp where comm is not null;
-- 小括号的使用  :因为不同的运算符的优先级别不同,加括号为了可读性
select * from emp where job = 'salesman' or 'clerk' and sal = 1250;-- and和or的优先级顺序是and > or
select * from emp where (job = 'salesman' or 'clerk') and sal = 1250;
select * from emp where job = 'salesman' or ('clerk' and sal = 1250);

3、使用函数进行查询

1、单行函数

-- 单行函数包含:
-- a.字符串函数
select ename,length(ename),substring(ename,2,3) from emp;
-- substring字符串截取,2:从字符下标为2开始,3:截取长度3    (下标从1开始)
-- b.数值函数
select abs(-5),ceil(5.3),floor(5.9),round(3.14) from dual; -- dual实际就是一个伪表 
select abs(-5) 绝对值,ceil(5.3) 向上取整,floor(5.9) 向下取整,round(3.14) 四舍五入;  -- 如果没有where条件的话,from dual可以省略不写
select ceil(sal) from emp;
select 10/3,10%3,mod(10,3) ;
-- c.日期与时间函数 
select * from emp;
select curdate(),curtime() ; -- curdate()年月日 curtime()时分秒
select now(),sysdate(),sleep(3),now(),sysdate() from dual; -- now(),sysdate() 年月日时分秒
insert into emp values (9999,'lili','SALASMAN',7698,now(),1000,null,30);
-- now()可以表示年月日时分秒,但是插入数据的时候还是要参照表的结构的
desc emp;-- desc是describe的缩写

-- d.流程函数
-- if相关
select empno,ename,sal,if(sal>=2500,'高薪','底薪') as '薪资等级' from emp; -- if-else 双分支结构
select empno,ename,sal,comm,sal+ifnull(comm,0) from emp; -- 如果comm是null,那么取值为0 -- 单分支
select nullif(1,1),nullif(1,2) from dual; --  如果value1等于value2,则返回null,否则返回value1  
-- case相关:
-- case等值判断
select empno,ename,job,
case job 
 when 'CLERK' then '店员'
 when 'SALESMAN'  then '销售'
 when 'MANAGER' then '经理'
 else '其他'
end '岗位',
sal from emp;
-- case区间判断:
select empno,ename,sal,
case 
 when sal<=1000 then 'A'
 when sal<=2000 then 'B'
 when sal<=3000 then 'C'
 else 'D'
end '工资等级',
deptno from emp;

-- e.JSON函数  
-- f.其他函数
select database(),user(),version() from dual;

2、多行函数

-- 多行函数:
select max(sal),min(sal),count(sal),sum(sal),sum(sal)/count(sal),avg(sal) from emp;
select * from emp;
-- 多行函数自动忽略null值
select max(comm),min(comm),count(comm),sum(comm),sum(comm)/count(comm),avg(comm) from emp;
-- max(),min(),count()针对所有类型   sum(),avg() 只针对数值型类型有效
select max(ename),min(ename),count(ename),sum(ename),avg(ename) from emp;
-- count --计数   
-- 统计表的记录数:方式1:
select * from emp;
select count(ename) from emp;
select count(*) from emp;
-- 统计表的记录数:方式2
select 1 from dual;
select 1 from emp;
select count(1) from emp;

4、group by分组

select * from emp;
-- 统计各个部门的平均工资 
select deptno,avg(sal) from emp; -- 字段和多行函数不可以同时使用
select deptno,avg(sal) from emp group by deptno; -- 字段和多行函数不可以同时使用,除非这个字段属于分组
select deptno,avg(sal) from emp group by deptno order by deptno desc;
-- 统计各个岗位的平均工资
select job,avg(sal) from emp group by job;
select job,lower(job),avg(sal) from emp group by job;

5、having分组后筛选

-- 统计各个部门的平均工资 ,只显示平均工资2000以上的  - 分组以后进行二次筛选 having
select deptno,avg(sal) from emp group by deptno having avg(sal) > 2000;
select deptno,avg(sal) 平均工资 from emp group by deptno having 平均工资 > 2000;
select deptno,avg(sal) 平均工资 from emp group by deptno having 平均工资 > 2000 order by deptno desc;
-- 统计各个岗位的平均工资,除了MANAGER
-- 方法1:
select job,avg(sal) from emp where job != 'MANAGER' group by job;
-- 方法2:
select job,avg(sal) from emp group by job having job != 'MANAGER' ;
-- where在分组前进行过滤的,having在分组后进行后滤。

单表查询小总结

虽然查询语句select可以写得很长,包含的条件啦、选择限制啦很多,但是它都是有顺序的,就像是把大象放进冰箱里一样有个先后执行顺序。SQL语句其实本身并不难,关键是要拿出做阅读理解那劲儿来把我们要实现的任务理解透彻,任务清晰后按照子句功能把他们放到各自对应的位置上去就OK啦!
【1】select语句总结
select column, group_function(column)
from table
[where condition]
[group by group_by_expression]
[having group_condition]
[order by column];

注意:顺序固定,不可以改变顺序

【2】select语句的执行顺序
from–where – group by– select having order by

二、多表查询

多表查询有交叉连接、自然连接、内连接和外连接。

1、交叉连接、自然连接、内连接

首先演示一下交叉连接、自然连接、内连接的用法。

-- 查询员工的编号,姓名,部门编号:
select * from emp;
select empno,ename,deptno from emp;
-- 查询员工的编号,姓名,部门编号,部门名称:
select * from emp; -- 14条记录
select * from dept; -- 4条记录 
-- 多表查询 :
-- 交叉连接:cross join
select * from emp cross join dept; -- 14*4 = 56条 笛卡尔乘积 : 没有实际意义,有理论意义
select * from emp join dept; -- cross 可以省略不写,mysql中可以,oracle中不可以
-- 自然连接:natural join 
-- 优点:自动匹配所有的同名列 ,同名列只展示一次 ,简单
select * from emp natural join dept;
select empno,ename,sal,dname,loc from emp natural join dept;
-- 缺点: 查询字段的时候,没有指定字段所属的数据库表,效率低
-- 解决: 指定表名:
select emp.empno,emp.ename,emp.sal,dept.dname,dept.loc,dept.deptno
from emp natural join dept;
-- 缺点:表名太长
-- 解决:表起别名
select e.empno,e.ename,e.sal,d.dname,d.loc,d.deptno
from emp e natural join dept d;
-- 自然连接 natural join 缺点:自动匹配表中所有的同名列,但是有时候我们希望只匹配部分同名列:
-- 解决: 内连接 - using子句:
select * from emp e inner join dept d -- inner可以不写
using(deptno); -- 这里不能写natural join了 ,这里是内连接
-- using缺点:关联的字段,必须是同名的 
-- 解决: 内连接 - on子句:
select * from emp e inner join dept d
on (e.deptno = d.deptno);
-- 多表连接查询的类型: 1.交叉连接  cross join  2. 自然连接  natural join  
-- 3. 内连接 - using子句   4.内连接 - on子句
-- 综合看:内连接 - on子句
select * from emp e inner join dept d
on (e.deptno = d.deptno)
where sal > 3500;
-- 条件:
-- 1.筛选条件  where  having
-- 2.连接条件 on,using,natural 
-- SQL99语法 :筛选条件和连接条件是分开的

2、外连接

-- 外连接:除了显示匹配的数据之外,还可以显示不匹配的数据
-- 左外连接: left outer join   -- 左面的那个表的信息,即使不匹配也可以查看出效果
select * from emp e left outer join dept d
on e.deptno = d.deptno;
-- 右外连接: right outer join   -- 右面的那个表的信息,即使不匹配也可以查看出效果
select * from emp e right outer join dept d
on e.deptno = d.deptno;
-- 全外连接  full outer join -- 这个语法在mysql中不支持,在oracle中支持 -- 展示左,右表全部不匹配的数据 
-- scott ,40号部门都可以看到
select * from emp e full outer join dept d
on e.deptno = d.deptno;
-- 解决mysql中不支持全外连接的问题:
select * from emp e left outer join dept d
on e.deptno = d.deptno
union -- 并集 去重 效率低
select * from emp e right outer join dept d
on e.deptno = d.deptno;
select * from emp e left outer join dept d
on e.deptno = d.deptno
union all-- 并集 不去重 效率高
select * from emp e right outer join dept d
on e.deptno = d.deptno;
-- mysql中对集合操作支持比较弱,只支持并集操作,交集,差集不支持(oracle中支持)
-- outer可以省略不写

三、子查询

子查询指的是一条SQL语句有多个select
子查询分为不相关子查询和相关子查询两种。

select * from emp where sal > (select sal from emp where ename = 'CLARK');
  • 一个命令解决问题 --》效率高

1、不相关子查询

【1】执行顺序:
先执行子查询,再执行外查询;
【2】不相关子查询:
子查询可以独立运行,称为不相关子查询
【3】不相关子查询分类:
根据子查询的结果行数,可以分为单行子查询和多行子查询。
(1)单行子查询
以下是单行子查询的几个例子:

-- 单行子查询:
-- 查询工资高于平均工资的雇员名字和工资。
select ename,sal from emp
where sal > (select avg(sal) from emp);
-- 查询和CLARK同一部门且比他工资低的雇员名字和工资。
select ename,sal from emp
where deptno = (select deptno from emp where ename = 'CLARK') 
      and 
      sal < (select sal from emp where ename = 'CLARK');
-- 查询职务和SCOTT相同,比SCOTT雇佣时间早的雇员信息  
select * from emp
where job = (select job from emp where ename = 'SCOTT') 
      and 
      hiredate < (select hiredate from emp where ename = 'SCOTT');

(2)多行子查询
以下是多行子查询的例子:

-- 多行子查询:
-- 【1】查询【部门20中职务同部门10的雇员一样的】雇员信息。
-- 查询雇员信息
select * from emp;
-- 查询部门20中的雇员信息
select * from emp where deptno = 20;-- CLERK,MANAGER,ANALYST
-- 部门10的雇员的职务:
select job from emp where deptno = 10; -- MANAGER,PRESIDENT,CLERK
-- 查询部门20中职务同部门10的雇员一样的雇员信息。
select * from emp where deptno = 20 
and job in (select job from emp where deptno = 10)
-- > Subquery returns more than 1 row
select * from emp where deptno = 20 and job = any(select job from emp where deptno = 10)
-- 【2】查询工资比所有的“SALESMAN”都高的雇员的编号、名字和工资。
-- 查询雇员的编号、名字和工资
select empno,ename,sal from emp;
-- “SALESMAN”的工资:
select sal from emp where job = 'SALESMAN';
-- 查询工资比所有的“SALESMAN”都高的雇员的编号、名字和工资。
-- 多行子查询:
select empno,ename,sal from emp where sal > all(select sal from emp where job = 'SALESMAN');
-- 单行子查询:
select empno,ename,sal from emp where sal > (select max(sal) from emp where job = 'SALESMAN');
-- 【3】查询工资低于任意一个“CLERK”的工资的雇员信息。  
-- 查询雇员信息
select * from emp;
-- 查询工资低于任意一个“CLERK”的工资的雇员信息
select * from emp where sal < any(select sal from emp where job = 'CLERK')
and job != 'CLERK'
-- 单行子查询:
select * from emp where sal < (select max(sal) from emp where job = 'CLERK') and job != 'CLERK'

2、相关子查询

子查询不可以独立运行,并且先运行外查询,再运行子查询。
举一个栗子:

-- 【2】查询本部门最高工资的员工   (相关子查询)
-- 方法1:通过不相关子查询实现:
select * from emp where deptno = 10 and sal = (select max(sal) from emp where deptno = 10);
union
select * from emp where deptno = 20 and sal = (select max(sal) from emp where deptno = 20);
union
select * from emp where deptno = 30 and sal = (select max(sal) from emp where deptno = 30);
-- 缺点:语句比较多,具体到底有多少个部分未知
-- 方法2: 相关子查询
select * from emp e where sal = (select max(sal) from emp where deptno = e.deptno) order by deptno;

以上就是本次学习笔记及练习的所有内容
end
bye~~

你可能感兴趣的:(mysql,数据库,sql)