SQL进阶--外连接的用法

用外连接进行行列转换(1):制作交叉表

drop table if exists course;
create table course(
name1 varchar(8),
course varchar(10)
);
insert into course values('赤井','SQL入门');
insert into course values('赤井','UNIX基础');
insert into course values('铃木','SQL入门');
insert into course values('工藤','SQL入门');
insert into course values('工藤','Java中级');
insert into course values('吉田','UNIX基础');
insert into course values('渡边','SQL入门');
-- 展开求交叉表:使用外连接
select distinct a.name1
       , case when b.name1 is not null then '0' else null end  'SQL入门'
       , case when c.name1 is not null then '0' else null end  'UNIX基础'
       , case when d.name1 is not null then '0' else null end  'Java中级'
  from course a left join course b on a.name1 = b.name1 and b.course = 'SQL入门'
  left join course c on a.name1 = c.name1 and c.course = 'UNIX基础'
  left join course d on a.name1 = d.name1 and d.course = 'Java中级'
;
-- 嵌套使用case表达式
select name1
       , case when sum(case when course = 'SQL入门' then 1 end) = 1 then '0'  end  'SQL入门'
       , case when sum(case when course = 'UNIX基础' then 1 end) = 1 then '0'  end  'UNIX基础'
       , case when sum(case when course = 'Java中级' then 1 end) = 1 then '0'  end  'Java中级'
  from course
 group by name1
;

结果图示:
这里写图片描述
外连接进行行列转换(2):汇总重复项于一列

drop table if exists personnel;
create table personnel(
employee varchar(8),
child_1 varchar(8),
child_2 varchar(8),
child_3 varchar(8)
);
insert into personnel values('赤井','一郎','二郎','三郎');
insert into personnel values('工藤','春子','夏子',NULL);
insert into personnel values('铃木','夏子',null,null);
insert into personnel values('吉田',null,null,null);
-- 列数据转换成行数据
select employee, child_1 as child from personnel
union all
select employee, child_2 as child from personnel
union all
select employee, child_3 as child from personnel
;

create view childern(child) as
select child_1 as child from personnel
union 
select child_2 as child from personnel
union 
select child_3 as child from personnel
;

select * from  childern;  -- 查看视图
-- 获取员工子女列表的SQL语句(没有孩子的员工也要输出)
select a.employee, b.child
  from personnel a
  left join childern b
         on b.child in (a.child_1, a.child_2, a.child_3)
;

用视图加左连接,用in作为连接条件,重名也不会有影响。
这里写图片描述

生成完整的嵌套式表侧栏

在交叉表里制作嵌套式表侧栏
drop table if exists TblAge;
create table TblAge(
age_class int4,
age_range varchar(20)
);
insert into tblage values(1, '21岁~30岁');
insert into tblage values(2, '31岁~40岁');
insert into tblage values(3, '41岁~50岁');
drop table if exists Tblsex;
create table Tblsex(
sex_cd varchar(8),
sex varchar(8)
);
insert into tblsex values('m','男');
insert into tblsex values('f','女');
drop table if exists Tblpop;
create table Tblpop(
pref_name varchar(8),
age_class int4,
sex_cd varchar(8),
population integer
);
insert into Tblpop values('秋田',1,'m',400);
insert into Tblpop values('秋田',3,'m',1000);
insert into Tblpop values('秋田',1,'f',800);
insert into Tblpop values('秋田',3,'f',1000);
insert into Tblpop values('青森',1,'m',700);
insert into Tblpop values('青森',1,'f',500);
insert into Tblpop values('青森',3,'f',800);
insert into Tblpop values('东京',1,'m',900);
insert into Tblpop values('东京',1,'f',1500);
insert into Tblpop values('东京',3,'f',1200);
insert into Tblpop values('千叶',1,'m',900);
insert into Tblpop values('千叶',1,'f',1000);
insert into Tblpop values('千叶',3,'f',900);


-- 生成完整的嵌套式表侧栏
select m.age_class   age_class
       , m.sex_cd    sex_cd
       , t.pop_tohoku pop_tohoku
       , t.pop_kanto  pop_kanto
  from (
        select age_class, sex_cd
          from tblage  cross join tblsex
        ) m -- 使用交叉连接生成两张主表的笛卡尔积
  left join
        (
        select age_class, sex_cd
               , sum(case when pref_name in ('青森','秋田') then population end) pop_tohoku
               , sum(case when pref_name in ('东京','千叶') then population end) pop_kanto
          from tblpop
         group by age_class, sex_cd
        ) t
         on m.age_class = t.age_class 
        and m.sex_cd = t.sex_cd
;

生成嵌套式表侧栏时,如果先生成主表的笛卡尔积再进行连接,就很容易完成。
这里写图片描述
作为乘法运算的连接

drop table if exists items;
create table items(
item_no int4,
item varchar(8)
);
insert into items values(10, 'FD');
insert into items values(20, 'CD-R');
insert into items values(30, 'MO');
insert into items values(40, 'DVD');

drop table if exists saleshistory;
create table saleshistory(
sale_date date,
item_no int4,
quantity int8
);
insert into saleshistory values(20071001, 10, 4);
insert into saleshistory values(20071001, 20, 10);
insert into saleshistory values(20071001, 30, 3);
insert into saleshistory values(20071003, 10, 32);
insert into saleshistory values(20071003, 30, 12);
insert into saleshistory values(20071004, 20, 22);
insert into saleshistory values(20071004, 30, 7);

select i.item_no, t.total_qty
  from items i 
  left join 
       (select item_no, sum(quantity) total_qty
          from saleshistory 
         group by item_no ) t
       on i.item_no = t.item_no
;
-- 先一对多的连接再聚合
select i.item_no, sum(t.quantity) total_qty
  from items i left join saleshistory  t 
    on i.item_no = t.item_no
 group by i.item_no
;

一对一或者一对多关系的两个集合,在进行连接操作行数不会(异常)增加
这里写图片描述

全外连接
full outer join = left outer join UNION right

drop table if exists class_a;
create table class_a(
id int4,
name1 varchar(10)
);
insert into class_a values(1, '田中');
insert into class_a values(2, '铃木');
insert into class_a values(3, '伊集院');

drop table if exists class_b;
create table class_b(
id int4,
name1 varchar(10)
);
insert into class_b values(1, '田中');
insert into class_b values(2, '铃木');
insert into class_b values(4, '西园寺');

-- 全外连接保留全部信息  full outer join = left outer join UNION right 
select  a.id id
       , a.name1 a_name
       , b.name1 b_name
  from class_a a left join class_b b on a.id = b.id
       union
select b.id id
       , a.name1 a_name
       , b.name1 b_name
  from class_a a right join class_b b on a.id = b.id
;

用外连接进行集合运算

-- 用外连接求差集:A-B
select a.id  id, a.name1 name1
  from class_a a left join class_b b
    on a.id = b.id
 where b.name1 is null
 ;
-- 用外连接求差集:B-A
select b.id  id, b.name1 name1
  from class_a a right join class_b b
    on a.id = b.id
 where a.name1 is null
 ;

练习


 -- 1-5-1 先连接还是先聚合  -- 用一对多关系加聚合函数来 减少临时视图
 select m.age_class   age_class
       , m.sex_cd    sex_cd
       , sum(case when pref_name in ('青森','秋田') then population end) pop_tohoku
       , sum(case when pref_name in ('东京','千叶') then population end) pop_kanto
  from (
        select age_class, sex_cd
          from tblage  cross join tblsex
        ) m -- 使用交叉连接生成两张主表的笛卡尔积
  left join tblpop t
         on m.age_class = t.age_class 
        and m.sex_cd = t.sex_cd
  group by m.age_class, m.sex_cd
;
-- 1-5-2 请留意孩子的人数
select a.employee, count(b.child) child_cnt
  from personnel a
  left join childern b
         on b.child in (a.child_1, a.child_2, a.child_3)
 group by a.employee
 order by a.employee
;

-- 1-5-3 全连接和MERGE运算符
-- 将两张表的信息汇总到一张表上
select a.id
       , case when a.name1 <> b.name1 or a.name1 is null then b.name1 else a.name1 end name_mix
  from class_a a left join class_b b 
    on a.id = b.id
union
select b.id
       , case when a.name1 <> b.name1 or a.name1 is null then b.name1 else a.name1 end name_mix
  from class_a a right join class_b b 
    on a.id = b.id
;

mysql用merge合并表

/*
merge合并表的要求
1.合并的表使用的必须是MyISAM引擎
2.表的结构必须一致,包括索引、字段类型、引擎和字符集
*/
drop table if exists class_a;
create table class_a(
id int4,
name1 varchar(10)
)engine = MyISAM default charset = utf8 auto_increment = 1;
insert into class_a values(1, '田中');
insert into class_a values(2, '铃木');
insert into class_a values(3, '伊集院');

drop table if exists class_b;
create table class_b like class_a;
insert into class_b values(1, '田中');
insert into class_b values(2, '内海');
insert into class_b values(4, '伊集院');

drop table if exists class_ab;
create table class_ab(
id int4,
name1 varchar(10)
)engine = merge union(class_a, class_b) default charset = utf8 auto_increment = 1;

select * from class_ab;

除了便于同时引用多个数据表而无需发出多条查询,MERGE数据表还提供了以下一些便利。

  • MERGE数据表可以用来创建一个尺寸超过各个MyISAM数据表所允许的最大长度逻辑单元
  • 你看一把经过压缩的数据表包括到MERGE数据表里。比如说,在某一年结束之后,你应该不会再往相应的日志文件里添加记录,所以你可以用myisampack工具压缩它以节省空间,而MERGE数据表仍可以像往常那样工作

内容多来自 《SQL进阶教材》,仅做笔记。练习部分代码均为原创。

猜你喜欢

转载自blog.csdn.net/scc_hy/article/details/80381388