其实前面说过数据库一句话:
这个就设计到一个E-R(entity -relatiosn 实体--联系)
模型,其主要设计到三个主要概念:实体集,属性,联系集。
联系集本质就是表之间的关系,现在聊一些表之间的关系,毕竟这个也是数据库中最重要的组成部分之一。毕竟对数数据不可能放在一张表中。
在看一下具体的数据:
部门表(dept)数据:
员工表(emp)数据:
工资等级表(slgrade):
可以看出表之emp
表和dept
表之间有关联,可以通过部门号进行关联。
其实在表与表之间的关联中会有一种错误,这个错误和数学中的迪卡尔积(作为一个了解)。
这个错误就是说两个表之间的数据,每一行都对应的匹配一遍,然后依次匹配,这个开结果
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp,dept;
可以看出这样搜索的结果数量竟然是两个表数据数量的积75=15*5。说实话这个就是一个错误的搜索而已,其没多少意义,或者可以称之为一个没有意义的搜索结果。
这个时候又想到既然有个表里面都有部门号deptno
,如果将其进行相等匹配呢?
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp,dept WHERE emp.deptno=dept.deptno;
这个可以看出数据进行了一一匹配,而且其后面可以得到所有的员工表。这个又涉及到一个为什么需要表.字段
其又什么意义呢?
这个很简单就是告诉数据库,这个字段是属于那个表的,如果单表或者两个表之间如果SELECT后面的字段不是两个表之间同名的,不写也不会报错,但是如果为了提高查询顺序建议带上表或者表的别名。当然也可以超过两个表。
可以看以下出出的错误:
SELECT deptno FROM emp,dept WHERE emp.deptno=dept.deptno;
这个分类其实上面等值连接,非等值连接是什么呢?
就是关联的时候,两个表之间的关系无法进行等于的时候,确定关系,其实这样说的话有点懵逼,还是老规矩看代码,
比如员工公司的等级,这个工资等级有个范围,而这个如何匹配呢?
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,salgrade.grade FROM emp,salgrade WHERE emp.sal BETWEEN salgrade.losal AND salgrade.hisal;
-- 或者
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,salgrade.grade FROM emp,salgrade WHERE emp.sal >= salgrade.losal AND emp.sal <= salgrade.hisal;
这样可以看出对员工表数据进行处理的时候,可以看出其不是用等号进行条件筛选的。
这个在表与表之间的关系的时候,说过就是自己关联自己表的就是自连接,与其它的表进行关联的就是非自连接。
上面演示的是非自连接,现在演示一个自连接,这个就是查看每个员工的领导是谁。
SELECT a.ename,b.ename mgrname FROM emp a,emp b WHERE a.mgr=b.empno;
前面两种还算很容易理解,其中最难里面的是这分类。
合并具有同一列的两个以上的表的行,结果只有两者都有的数据。而上面的几乎都是内连接。其实这个有点像是数学的交集概念。如下
如果无法两者都满足的话就会舍去,所以
SELECT a.ename,b.ename mgrname FROM emp a,emp b WHERE a.mgr=b.empno;
为啥得到了15行而不是16行,毕竟公司都有老板,老板虽然有工号,但是没有上级的工号,所以只有15行。
在聊外连接的时候先补充一个内容。
这个就需要补充关键字:UNION 和 UNION ALL
UNION 操作符用于合并两个或多个 SELECT 语句的结果集。请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。
现在我们来尝试写一下
SELECT deptno FROM emp
UNION
SELECT deptno FROM dept
这个可以看员工表中 部门号有一个是NULL,以及部门表中部门号40,50中没有员工,因为两表查询都是一个字段而且的都是部门号通过UNION
将其连接。可以看出将两个表中所有部门号的内容都取出来了。
这个地方要注意一点,其关心的是是否前后表的列是否相同,不关心是否是一样的字段名和类型
。例如下:
SELECT deptno FROM emp
UNION
SELECT dname FROM dept
但是一般也不会将两张不同数据的表进行合并,这里只是补充而已。
那UNION ALL
又是什么数据呢?
SELECT deptno FROM emp
UNION ALL
SELECT deptno FROM dept
这样就可以看出一件事,那就是UNION ALL
相对于UNION
不会去重。
所以如果一般的时候在UNION ALL
和UNION
都可以实现的功能的时候,为了性能更快侧重选择使用UNION ALL
,毕竟这种方式返回的数据更快,因为其不会去重。
为什么会补充合并两个表的关键字呢,下面聊外连接的时候就明白了,
说实话看图比文字更容易理解,但是表达出来的意思会很绕。
外连接其实就是上面空白的地方,这个又分三种可能
这个自然固定的SQL可以使用,需要用的就是JOIN..... ON....
这个关键字组合,这个组合也是可以实现内连接的,其实这个组合是SQL99引入的。
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp,dept WHERE emp.deptno=dept.deptno;
-- 这个是上面根据= 实现的一个内连接 这个是SQL92的一种写法
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp INNER JOIN dept ON emp.deptno=dept.deptno;
-- 这个INNER 可以省略写
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp JOIN dept ON emp.deptno=dept.deptno;
当然JOIN ON
也可以跟多个表的:
-- 当然如果多个表也是可以使用的
SELECT .... FROM A JOIN B on A.a=B.a
JOIN C on B.b=C.b
表(>=3)等值内链接(inner join)最好用SQL92语法,链接逻辑简单,代码语句少,可读性强。(也就是使用=)
现在开始来一个左外连接。
这个注意一些,用的员工表在左边,部门表在右,而且员工中有一个没有部门号。
这个需要用到关键字A LEFT OUTER JOIN B ON A.a=B.a 或者 A LEFT JOIN B ON A.a=B.a
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp LEFT OUTER JOIN dept ON emp.deptno=dept.deptno;
-- 这个可以省略 OUTER 这个关键字
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp LEFT JOIN dept ON emp.deptno=dept.deptno;
可以看出以左边表为基,如果右表不含有匹配的字段内容就会补充NULL.
因为可以省略OUTER这个关键字可以省略,所以也叫做左连接。
前面说到SQL92其实也有外连接的操作,不过其不用关键字,而是符号+
,不过MYSQL不支持,而ORCLE支持,如下:
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp,dept WHERE emp.deptno=dept.deptno(+);
-- 左连接的时候(+)放在右表的关键字后面
这个其实和左外连接本质上一样的,只不过其关键字是 A RIGHT OUTER JOIN B ON A.a=B.a 或者 A RIGHT JOIN B ON A.a=B.a
这个注意一些,用的员工表在左边,部门表在右,而且部门表中有的部门没有员工对应。
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp RIGHT OUTER JOIN dept ON emp.deptno=dept.deptno;
-- 这个可以省略 OUTER 这个关键字
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp RIGHT JOIN dept ON emp.deptno=dept.deptno;
当然也有SQL92写法,左外说过,所以不在陈述。
这个才是最难的,先说一下SQL99中的写法 A FULL OUTER JOIN B ON A.a=B.a 或者 A FULL JOIN B ON A.a=B.a
还是用上面的两个表用的员工表在左边,部门表在右
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp FULL OUTER JOIN dept ON emp.deptno=dept.deptno;
-- 这个可以省略 OUTER 这个关键字
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp FULL JOIN dept ON emp.deptno=dept.deptno;
先声明一下,写的不错,**但是MYSQL 不支持A FULL OUTER JOIN B ON A.a=B.a 或者 A FULL JOIN B ON A.a=B.a
**
这个ORACLE支持,所以有条件的朋友,可以在ORACLE中进行尝试。
但是既然聊MYSQL自然,就要想法将其搞定。前面聊了UNION
和UNION ALL
.
而这个时候可以通过其它的方式通过合并表实现这个满外连接。
根据已知的语句SQL99中的JOIN ON
可以得到以下的表:
而MYSQL不支持A FULL OUTER JOIN B ON A.a=B.a 或者 A FULL JOIN B ON A.a=B.a
。
但是MYSQL却支持 左外和右外,所以我们可以也就是上图中的 1和2 ,而1,2通过WHERE 进行限制可以得到4和5.然后通过4合并2或者5合并1就得到类似上图中的6的效果。现在我们搞一波。
满连接通过图中的1和5进行合并:
途中1的效果左连接,就不需要演示,而这个5就是对右边连接进行WHERE 条件语言进行限制,这个可以实现吗?无论左右外连接都是少的一方会自动补充为NULL,所以可以可以用左边数据为空的,也就是图5 的内容。猜想后用代码演示:
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp RIGHT JOIN dept ON emp.deptno=dept.deptno WHERE emp.deptno IS NULL;
可以看出满足了我们的猜想。那现在将其和左外连接进行合并:
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp LEFT JOIN dept ON emp.deptno=dept.deptno
UNION ALL
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp RIGHT JOIN dept ON emp.deptno=dept.deptno WHERE emp.deptno IS NULL;
满外连接用4和2并的效果:
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp LEFT JOIN dept ON emp.deptno=dept.deptno WHERE dept.deptno IS NULL
UNION ALL
SELECT emp.empno,emp.ename,emp.job,emp.mgr,emp.hiredate,emp.sal,emp.comm,emp.deptno,dept.deptno,dept.dname,dept.loc FROM emp RIGHT JOIN dept ON emp.deptno=dept.deptno ;
所以这个时候,需要仔细看需求,比如某表的全部,以及其对应的另一个。这样的一般都是外连接,然后再仔细分析是左右外连接还是满外连接。