SQL进阶——Having子句

运行环境 win10 / oracle(DB11g)

1、可以出勤的队伍

思路:
一个集合中,如果元素最大值和最小值相等,那么这个集合中肯定只用一种值

-- 创表
create table teams_10
(
    member varchar(10),
    team_id integer,
    status1 varchar(10)
);

insert into teams_10 values('乔', 1, '待命');
insert into teams_10 values('肯', 1, '出勤中');
insert into teams_10 values('米克', 1, '待命');
insert into teams_10 values('卡伦', 2, '出勤中');
insert into teams_10 values('凯斯', 2, '休息');
insert into teams_10 values('简', 3, '待命');
insert into teams_10 values('哈特', 3, '待命');
insert into teams_10 values('迪克', 3, '待命');
insert into teams_10 values('贝斯', 4, '待命');
insert into teams_10 values('阿伦', 5, '出勤中');
insert into teams_10 values('罗伯特', 5, '休息');
insert into teams_10 values('卡跟', 5, '待命');

SQL解决:

-- 1-直接找出可出勤的队
select team_id
  from teams_10
 group by team_id
having max(status1) = '待命' 
   and min(status1) = '待命'
;

-- 2-对出勤的队打标签
select team_id
       ,case when max(status1) = '待命' and min(status1) = '待命' 
             then '全部在待命'
        else '队长人手不够' end as status1
  from teams_10
 group by team_id
;

结果:

-- 1
   TEAM_ID
----------
         4
         3


-- 2
   TEAM_ID STATUS1
---------- ------------
         1 队长人手不够
         2 队长人手不够
         4 全部在待命
         5 队长人手不够
         3 全部在待命

2、查找重复的元素

-- 创表
create table materials_10
(
    center varchar(10),
    recieve_date date,
    material varchar(6)
)
;

insert into materials_10 values ('东京',to_date('2007-04-01','yyyy-mm-dd'),'锡');
insert into materials_10 values ('东京',to_date('2007-04-12','yyyy-mm-dd'),'锌');
insert into materials_10 values ('东京',to_date('2007-05-17','yyyy-mm-dd'),'铝');
insert into materials_10 values ('东京',to_date('2007-05-20','yyyy-mm-dd'),'锌');
insert into materials_10 values ('大阪',to_date('2007-04-20','yyyy-mm-dd'),'铜');
insert into materials_10 values ('大阪',to_date('2007-04-22','yyyy-mm-dd'),'镍');
insert into materials_10 values ('大阪',to_date('2007-04-29','yyyy-mm-dd'),'铅');
insert into materials_10 values ('名古屋',to_date('2007-03-15','yyyy-mm-dd'),'钛');
insert into materials_10 values ('名古屋',to_date('2007-04-01','yyyy-mm-dd'),'钢');
insert into materials_10 values ('名古屋',to_date('2007-04-24','yyyy-mm-dd'),'钢');
insert into materials_10 values ('名古屋',to_date('2007-05-02','yyyy-mm-dd'),'镁');
insert into materials_10 values ('名古屋',to_date('2007-05-10','yyyy-mm-dd'),'钛');
insert into materials_10 values ('福冈',to_date('2007-05-10','yyyy-mm-dd'),'锌');
insert into materials_10 values ('福冈',to_date('2007-05-28','yyyy-mm-dd'),'锡');

SQL解决:

select center 
  from materials_10
 group by center
having count(material) <> count(distinct material)
;

结果:

CENTER
----------
名古屋
东京

用Having可以找出那个生产中心有存在重复材料生产,但是不能解决是哪一种材料重复了,为实现该目的,可以用exists ,他可以呈现更多的结果

select center, material
  from materials_10 m1
 where exists 
      (select * 
         from materials_10 m2 
        where m1.center = m2.center
          and m1.recieve_date <> m2.recieve_date 
          and m1.material = m2.material
      )
;

-- 结果
CENTER     MATERI
---------- ------
东京       锌
东京       锌
名古屋     钛
名古屋     钢
名古屋     钢
名古屋     钛

3、为集合设置详细的条件

思路:
通过case表达式生成特征函数,以满足各种条件

-- 创表
create table testresults_10
(
    student_id varchar(4),
    classs varchar(2),
    sex varchar(4),
    score integer
)
;

insert into testresults_10 values('001','A','男',100);
insert into testresults_10 values('002','A','女',100);
insert into testresults_10 values('003','A','女',49);
insert into testresults_10 values('004','A','男',30);
insert into testresults_10 values('005','B','女',100);
insert into testresults_10 values('006','B','男',92);
insert into testresults_10 values('007','B','男',80);
insert into testresults_10 values('008','B','男',80);
insert into testresults_10 values('009','B','女',10);
insert into testresults_10 values('010','C','男',92);
insert into testresults_10 values('011','C','男',80);
insert into testresults_10 values('012','C','女',21);
insert into testresults_10 values('013','D','女',100);
insert into testresults_10 values('014','D','女',0);
insert into testresults_10 values('015','D','女',0);

SQL解决:

-- 查询75%以上的学生分数都在80分以上的班级
select classs
  from testresults_10
 group by classs
having count(*)*0.75
       <= sum(case when score >=80 then 1 else 0 end)
;
-- 结果
CL
--
B


-- 查询出分数在50分以上的男生的人数比分数在50分以上的女生多的班级
select classs
  from testresults_10
 group by classs
having sum(case when score >= 50 and sex = '男' then 1 else 0 end)
       > sum(case when score >= 50 and sex = '女' then 1 else 0 end)
;
-- 结果
CL
--
B
C

-- 查询出女生平均分比男生平均分高的班级(对空集求平均为NULL)
select classs
  from testresults_10
 group by classs
having avg(case when sex = '男' then score end )
        < avg(case when sex = '女' then score end )
;
-- 结果
CL
--
A

练习

1、查重复

-- 创表
create table materials_10_t
(
    center varchar(10),
    recieve_date date,
    material varchar(6),
    orgland varchar(6)
)
;

insert into materials_10_t values ('东京',to_date('2007-04-01','yyyy-mm-dd'),'锡','智利');
insert into materials_10_t values ('东京',to_date('2007-04-12','yyyy-mm-dd'),'锌','泰国');
insert into materials_10_t values ('东京',to_date('2007-05-17','yyyy-mm-dd'),'铝','巴西');
insert into materials_10_t values ('东京',to_date('2007-05-20','yyyy-mm-dd'),'锌','泰国');
insert into materials_10_t values ('大阪',to_date('2007-04-20','yyyy-mm-dd'),'铜','澳大利亚');
insert into materials_10_t values ('大阪',to_date('2007-04-22','yyyy-mm-dd'),'镍','南非');
insert into materials_10_t values ('大阪',to_date('2007-04-29','yyyy-mm-dd'),'铅','印度');
insert into materials_10_t values ('名古屋',to_date('2007-03-15','yyyy-mm-dd'),'钛','玻利维亚');
insert into materials_10_t values ('名古屋',to_date('2007-04-01','yyyy-mm-dd'),'钢','智利');
insert into materials_10_t values ('名古屋',to_date('2007-04-24','yyyy-mm-dd'),'钢','阿根廷');
insert into materials_10_t values ('名古屋',to_date('2007-05-02','yyyy-mm-dd'),'镁','智利');
insert into materials_10_t values ('名古屋',to_date('2007-05-10','yyyy-mm-dd'),'钛','泰国');
insert into materials_10_t values ('福冈',to_date('2007-05-10','yyyy-mm-dd'),'锌','美国');
insert into materials_10_t values ('福冈',to_date('2007-05-28','yyyy-mm-dd'),'锡','俄罗斯');

SQL解决:

-- 1、exists
select center, material, orgland
  from materials_10_t m1
 where exists 
      (select * 
         from materials_10_t m2 
        where m1.center = m2.center
          and m1.recieve_date <> m2.recieve_date 
          and m1.material = m2.material 
          and m1.orgland = m2.orgland
      )
;
-- 2、having
select center, orgland
  from materials_10_t
 group by center, orgland
having count(material) <> count(distinct material)
   and count(orgland)  <> count(distinct orgland)
;

结果:

-- 1
CENTER     MATERI ORGLAN
---------- ------ ------
东京       锌     泰国
东京       锌     泰国

-- 2
CENTER     ORGLAN
---------- ------
东京       泰国

2、多条件的特征函数

(数学的分数在80分以上/语文的分数在50分以上)

-- 创表
create table testscores(
    student_id INTEGER,
    subject1    varchar(10),
    score       INTEGER
);
insert into testscores values(100, '数学', 100);
insert into testscores values(100, '语文', 80);
insert into testscores values(100, '理化', 80);
insert into testscores values(200, '数学', 80);
insert into testscores values(200, '语文', 95);
insert into testscores values(300, '数学', 40);
insert into testscores values(300, '语文', 90);
insert into testscores values(300, '社会', 55);
insert into testscores values(400, '数学', 80);

SQL解决:

-- 数学的分数在80分以上/语文的分数在50分以上
-- 回忆exists
select *
  from testscores t1
 where not EXISTS
        (  select *
             from testscores t2 
            where t1.student_id = t2.student_id
              and ( (subject1 = '数学' and score <= 80)
               or   (subject1 = '语文' and score <= 50) )
           )
;

-- 用having  空集为NULL
select student_id
  from testscores
 group by student_id
having 2 = sum(case when subject1 = '数学' and score > 80 then 1
                    when subject1 = '语文' and score > 50 then 1
                    end)
;

结果:

-- 1

STUDENT_ID SUBJECT1        SCORE
---------- ---------- ----------
       100 理化               80
       100 语文               80
       100 数学              100

-- 2

STUDENT_ID
----------
       100

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

你可能感兴趣的:(sql)