第三范式( 3NF ):如果关系模式 R ( U , F )中的所有非主属性对任何候选关键字都不存在传递信赖,则称关系 R 是属于第三范式的。
例:如 S1 ( SNO , SNAME , DNO , DNAME , LOCATION ) 各属性分别代表学号,姓名,所在系,系名称,系地址。
关键字 SNO 决定各个属性。由于是单个关键字,没有部分依赖的问题,肯定是 2NF 。但这关系肯定有大量的冗余,有关学生所在的几个属性 DNO , DNAME , LOCATION 将重复存储,插入,删除和修改时也将产生类似以上例的情况。
原因:关系中存在传递依赖造成的。由于 SNAME 和 DNO 是依赖 SNO ,而 DNAME 和 LOCATION 是依赖 DNO 的,即通过一个学号可以知道该学生的姓名以及他所在系代码,但无法由学号知道系地址,学号和系地址间是通过学号所对应的学生的所在系关联的, 因此关键字 SNO 对 LOCATION 函数决定是通过传递依赖 DNO -> LOCATION 实现的。也就是说, SNO 不直接决定非主属性 LOCATION 。
解决目地:每个关系模式中不能留有传递依赖。
解决方法:分为两个关系 S ( SNO , SNAME , DNO ), D ( DNO , DNAME , LOCATION )
注意:关系 S 中不能没有外关键字 DNO 。否则两个关系之间失去联系。
在数据库的设计过程中常常按照第三范式来设计数据库,当然在有些场合为优化数据库的性能而增加了相关冗余字段以使表的结构不符合 3NF ,在多数场合中,没有一张表能完整的发挥客户所需要的结果集。这样就需要通过联接多张在逻辑上存在依赖关系的表,选择你所需要的数据。
在使用 JOIN 前,必须明白是通过联接,根据各个表之间的逻辑关系从相关表中检索数据。通过 SQL Server 自带帮助文件,可以清楚的知道:可在 FROM 或 WHERE 子句中指定联接。
1.1. 在 WHERE 子句中指定联接
下例使用 WHERE 子句进行表之间的
SELECT A.SYMBOL,A.SNAME,B.TDATE,B.CLOSE
FROM SECURITYCODE A,DAYQUOTE B
WHERE A.SYMBOL =B.SYMBOL
AND B.TDATE >= A.LISTDATE
AND A.SYMBOL LIKE ‘600%’
在上例中联接的表 A 与表 B 通过 A.SYMBOL =B.SYMBOL 这个条件联接,筛选条件为 B.TDATE 不小于 A. LISTDATE 。
在 WHERE 子句中指定联接,对于较简单的联接,使用这种方式可能较方便,但综合来说,不推荐使用该语法联接表。
1.2. 在 FROM 子句中指定联接
拿上面的例子来详细说,表 SECURITYCODE 中主要存放证券代码的基本信息,表中的 SYMBOL 代表股票代码, SNAME 代表股票名称, LISTDATE 代表上市日期;表 DAYQUOTE 中主要存放股票的每日行情。 SYMBOL 代表股票代码, TDATE 代表交易日期, CLOSE 代表收盘价。
我现在的取值逻辑是:取出股票代码以 600 打头的股票自上市日期以来的所有交易日的收盘价。将 SECURITYCODE 与 DAYQUOTE 通过 SYMBOL 联接。
SELECT A.SYMBOL,A.SNAME,B.TDATE,B. CLOSE
FROM SECURITYCODE A
JOIN DAYQUOTE B
ON A.SYMBOL =B.SYMBOL
WHERE B.TDATE >= A.LISTDATE
AND A.SYMBOL LIKE ‘600%’
ORDER BY A.SYMBOL,B.TDATE
对于使用 FROM 子句方式联接表,可以很清楚的看出表之间的联接条件。就可读性以及后续的可修改性与 WHERE 子句相比有较大的优势。
下面就联接的方式引用帮助文件中的具体介绍,联接可以分为以下几种:
1.3. 内联接
内联接(典型的联接运算,使用像 = 或 <> 之类的比较运算符)。包括相等联接和自然联接。
内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行。
在 SQL-92 标准中,内联接可在 FROM 或 WHERE 子句中指定。这是 WHERE 子句中唯一一种 SQL-92 支持的联接类型。 WHERE 子句中指定的内联接称为旧式内联接。
内联接一般常见写法:
SELECT A.COLUMN1,[A.COLUMN2],B.COLUMN1,[B.COLUMN2]
FROM TABLE1 A
[INNER] JOIN TABLE2 B
ON A.COLUMN0 = B.COLUMN0
在查询分析器中使用 INNER JOIN 时常常省略 INNER 。
1.4. 外联接
外联接可以是左向外联接、右向外联接或完整外部联接。
在 FROM 子句中指定外联接时,可以由下列几组关键字中的一组指定:
LEFT JOIN 或 LEFT OUTER JOIN
左向外联接的结果集包括 LEFT OUTER 子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。
RIGHT JOIN 或 RIGHT OUTER JOIN
右向外联接是左向外联接的反向联接。将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。
FULL JOIN 或 FULL OUTER JOIN
完整外部联接返回左表和右表中的所有行。当某行在另一个表中没有匹配行时,则另一个表的选择列表列包含空值。如果表之间有匹配行,则整个结果集行包含基表的数据值。
仅当至少有一个同属于两表的行符合联接条件时,内联接才返回行。内联接消除与另一个表中的任何行不匹配的行。而外联接会返回 FROM 子句中提到的至少一个表或视图的所有行,只要这些行符合任何 WHERE 或 HAVING 搜索条件。将检索通过左向外联接引用的左表的所有行,以及通过右向外联接引用的右表的所有行。完整外部联接中两个表的所有行都将返回。
外联接的中常见的是 LEFT JOIN ,将 LEFT JOIN 用熟已经可以解决大半问题了。
外联接的一般写法:
SELECT A.COLUMN1,[A.COLUMN2],B.COLUMN1,[B.COLUMN2]
FROM TABLE1 A
LEFT|RIGHT|FULL [OUTER] JOIN TABLE2 B
ON A.COLUMN0 = B.COLUMN0
在查询分析器中使用 OUTER JOIN 时常常省略 OUTER 。 LEFT 和 RIGHT 只是方向问题,在特定场合下, FULL OUTER JOIN 相当于 LEFT OUTER JOIN 和 RIGHT OUTER JOIN 的消除重复行的合集。
1.5. 交叉联接
交叉联接返回左表中的所有行,左表中的每一行与右表中的所有行组合。交叉联接也称作笛卡尔积。
没有 WHERE 子句的交叉联接将产生联接所涉及的表的笛卡尔积。第一个表的行数乘以第二个表的行数等于笛卡尔积结果集的大小。也就是说在没有 WHERE 子句的情况下,若表 A 有 3 行记录,表 B 有 6 行记录 : :
SELECT A.*,B.* FROM 表A CROSS JOIN 表B
那以上语句会返回 18 行记录。