用外连接进行行列转换(1)(行->列):制作交叉表
这里有一张用于管理员工学习过的培训课程的表,如下所示。
利用上面这张表生成下面这样一张交叉表(“课程学习记录一览表”)。
1 | -- 外连接 |
这种做法具有比较直观和易于理解的优点,但是因为大量用到了内嵌视图和连接操作,代码会显得很臃肿。而且,随着表头列数的增加,性能也会恶化。
一般情况下,外连接都可以用标量子查询替代,因此可以像下面这样写。
1 | -- 标量子查询 |
这种做法不仅利于应对需求变更,对于需要动态生成SQL的系统也是很有好处的。缺点是性能不太好,目前在SELECT子句中使用标量子查询(或者关联子查询)的话,性能开销还是相当大的。
1 | -- CASE表达式 |
用外连接进行行列转换(2)(列->行):汇总重复项于一列
假设存在下面这样一张表。
需要做的是将这张表转换成行格式的数据。不过,根据具体需求,有时需要把没有孩子的吉田也留在表里,像下面这张“员工子女列表”这样。
1 | CREATE VIEW Children(child) |
在交叉表里制作嵌套式表侧栏
在生成统计表的工作中,经常会有制作嵌套式表头和表侧栏的需求。例如:表TblPop是一张按照县、年龄层级和性别统计的人口分布表,要求根据表TblPop生成交叉表“包含嵌套式表侧栏的统计表”。
1 | SELECT MASTER.age_class AS age_class, |
技巧是对表TblAge和表TblSex进行交叉连接运算,生成笛卡尔积。
作为乘法运算的连接
有下面的商品主表和商品销售历史管理表。
使用这两张表生成一张统计表,以商品为单位汇总出各自的销量。
1 | -- 通过在连接前聚合来创建一对一的关系 |
这种做法的确是正确的,代码也很容易理解。但是,如果从性能角度考虑,这条SQL语句还是有些问题的。比如临时视图SH的数据需要临时存储在内存里,还有就是虽然通过聚合将item_no变成了主键,但是SH上却不存在主键索引,因此也就无法利用索引优化查询。
1 | -- 先进行一对多的连接再聚合 |
这种做法代码更简洁,而且没有使用临时视图,所以性能也会有所改善。技巧是一对一或一对多关系的两个集合,在进行连接操作后行数不会(异常地)增加。
全外连接
全外连接是能够从这样两张内容不一致的表里,没有遗漏地获取全部信息的方法,所以也可以理解成“把两张表都当作主表来使用”的连接。
1 | SELECT COALESCE(A.id, B.id) AS id, |
用外连接求差集:A-B
1 | SELECT A.id AS id, A.name AS A_name |
用外连接求差集:B-A
1 | SELECT B.id AS id, B.name AS B_name |
用全外连接求异或集
SQL没有定义求异或集的运算符。如果用集合运算符,可以有两种方法。一种是(A UNION B) EXCEPT (A INTERSECT B),另一种是(A EXCEPT B) UNION (B EXCEPT A)。两种方法都比较麻烦,性能开销也会增大。
1 | SELECT COALESCE(A.id, B.id) AS id, |
用外连接求集合的商
使用“HAVING子句的力量”里的表Items和表ShopItems,可以像下面这样写。
1 | SELECT DISTINCT shop |