SQL练习题二

21.查询员工的累计薪水(☆)

Employee 表保存了一年内的薪水信息。
请你编写 SQL 语句,对于每个员工,查询他除最近一个月(即最大月)之外,剩下每个月的近三个月的累计薪水(不足三个月也要计算)。
结果请按 Id 升序,然后按 Month 降序显示

| Id | Month | Salary |
|----|-------|--------|
| 1  | 1     | 20     |
| 2  | 1     | 20     |
| 1  | 2     | 30     |
| 2  | 2     | 30     |
| 3  | 2     | 40     |
| 1  | 3     | 40     |
| 3  | 3     | 60     |
| 1  | 4     | 60     |
| 3  | 4     | 70     |

-- 输出
| Id | Month | Salary |
|----|-------|--------|
| 1  | 3     | 90     |
| 1  | 2     | 50     |
| 1  | 1     | 20     |
| 2  | 1     | 20     |
| 3  | 3     | 100    |
| 3  | 2     | 40     |

-- lag与窗口的大小
-- 理解求每个月最近三个月 rows between 2 preceding and current row
select 
Id,Month,
sum(Salary) over(partition by Id order by Month rows between 2 preceding and current row ) Salary
from 
(
select
Id,Month,Salary,
lag(Salary,1,0) over(partition by Id order by Month desc) rn 
from
Employee
) 
where rn != 0
order by Id,Month desc
-- 这也可以非常好的方法,使用两个偏移量函数,求总和,使用一个序列函数来过滤出最大月份的情况,不用窗口就是自连接也可以写
SELECT ID, MONTH, SALARY + IFNULL(L1, 0) + IFNULL(L2, 0) AS SALARY
FROM (SELECT *, LAG(SALARY, 1) OVER(PARTITION BY ID ORDER BY MONTH) AS L1, LAG(SALARY, 2) OVER(PARTITION BY ID ORDER BY MONTH) AS L2, RANK() OVER(PARTITION BY ID ORDER BY MONTH DESC) AS `RANK`
      FROM EMPLOYEE) AS A
WHERE `RANK` > 1
ORDER BY 1, 2 DESC;

22.统计各个专业学生数

一所大学有 2 个数据表,分别是 student 和 department ,这两个表保存着每个专业的学生数据和院系数据。
写一个查询语句,查询 department 表中每个专业的学生人数 (即使没有学生的专业也需列出)。
将你的查询结果按照学生人数降序排列。 如果有两个或两个以上专业有相同的学生数目,将这些部门按照部门名字的字典序从小到大排列

student 表格如下:

| Column Name  | Type      |
|--------------|-----------|
| student_id   | Integer   |
| student_name | String    |
| gender       | Character |
| dept_id      | Integer   |
其中, student_id 是学生的学号, student_name 是学生的姓名, gender 是学生的性别, dept_id 是学生所属专业的专业编号。

department 表格如下:

| Column Name | Type    |
|-------------|---------|
| dept_id     | Integer |
| dept_name   | String  |

-- 考察的就是count(*) count(表达式) 为null的处理
select 
dept_name,
count(student_name) as student_number
from
(
select 
dept_name,student_name
from 
department 
left join 
student
on department.dept_id = student.dept_id
) t
group by dept_name
order by student_number desc ,dept_name

23.推荐人

给定表 customer ,里面保存了所有客户信息和他们的推荐人。

+------+------+-----------+
| id   | name | referee_id|
+------+------+-----------+
|    1 | Will |      NULL |
|    2 | Jane |      NULL |
|    3 | Alex |         2 |
|    4 | Bill |      NULL |
|    5 | Zack |         1 |
|    6 | Mark |         2 |
+------+------+-----------+
写一个查询语句,返回一个编号列表,列表中编号的推荐人的编号都 不是 2。

简单题,但是是我的知识的盲区了,官方的答案

MySQL 使用三值逻辑 —— TRUE, FALSE 和 UNKNOWN。任何与 NULL 值进行的比较都会与第三种值 UNKNOWN 做比较。这个“任何值”包括 NULL 本身!这就是为什么 MySQL 提供 IS NULL 和 IS NOT NULL 两种操作来对 NULL 特殊判断。
因此,在 WHERE 语句中我们需要做一个额外的条件判断 `referee_id IS NULL'。

避免错误的秘诀在于使用 IS NULL 或者 IS NOT NULL 两种操作来对 NULL 值做特殊判断
SELECT name FROM customer WHERE referee_id = NULL OR referee_id <> 2;

-- 这样写法的执行的速度大于用 != 2,为什么???
select name
from customer
where referee_id < 2 or referee_id > 2 or referee_id is null

24.2016年的投资(窗口写法)

写一个查询语句,将 2016 年 (TIV_2016) 所有成功投资的金额加起来,保留 2 位小数
对于一个投保人,他在 2016 年成功投资的条件是:
他在 2015 年的投保额 (TIV_2015) 至少跟一个其他投保人在 2015 年的投保额相同。
他所在的城市必须与其他投保人都不同(也就是说维度和经度不能跟其他任何一个投保人完全相同)

输入格式:
表 insurance 格式如下:

| Column Name | Type          |
|-------------|---------------|
| PID         | INTEGER(11)   |
| TIV_2015    | NUMERIC(15,2) |
| TIV_2016    | NUMERIC(15,2) |
| LAT         | NUMERIC(5,2)  |
| LON         | NUMERIC(5,2)  |
PID 字段是投保人的投保编号, TIV_2015 是该投保人在2015年的总投保金额, TIV_2016 是该投保人在2016年的投保金额, LAT 是投保人所在城市的维度, LON 是投保人所在城市的经度。

样例输入

| PID | TIV_2015 | TIV_2016 | LAT | LON |
|-----|----------|----------|-----|-----|
| 1   | 10       | 5        | 10  | 10  |
| 2   | 20       | 20       | 20  | 20  |
| 3   | 10       | 30       | 20  | 20  |
| 4   | 10       | 40       | 40  | 40  |

样例输出

| TIV_2016 |
|----------|
| 45.00    |
-- 我的写法使用concat函数
select 
round(sum(TIV_2016) , 2) as TIV_2016
from 
insurance
where TIV_2015 in 
(
select 
TIV_2015
from
insurance
group by TIV_2015
having count(TIV_2015) >1
)
and concat(lat, lon) in
(
select
concat(lat, lon)
from
insurance
group by lat , lon
having count(*) = 1
)
-- 窗口写法
SELECT ROUND(SUM(TIV_2016), 2) AS TIV_2016
FROM (SELECT *, COUNT(*) OVER(PARTITION BY TIV_2015) AS C1, COUNT(*) OVER(PARTITION BY LAT, LON) AS C2
      FROM INSURANCE) AS A
WHERE C1 > 1
AND C2 = 1;

25.订单最多的客户

-- 简单题,考虑如果并列第一的话使用where 1
select
customer_number
from
orders
group by customer_number
order by  count(1) desc 
limit 1

26.大的国家(union 和 or 哪个的效率高)

https://stackoverflow.com/questions/13750475/sql-performance-union-vs-o

为了不影响性能,可以使用union all,然后手动排序去重

这里有张 World 表

+-----------------+------------+------------+--------------+---------------+
| name            | continent  | area       | population   | gdp           |
+-----------------+------------+------------+--------------+---------------+
| Afghanistan     | Asia       | 652230     | 25500100     | 20343000      |
| Albania         | Europe     | 28748      | 2831741      | 12960000      |
| Algeria         | Africa     | 2381741    | 37100000     | 188681000     |
| Andorra         | Europe     | 468        | 78115        | 3712000       |
| Angola          | Africa     | 1246700    | 20609294     | 100990000     |
+-----------------+------------+------------+--------------+---------------+
如果一个国家的面积超过 300 万平方公里,或者人口超过 2500 万,那么这个国家就是大国家。

编写一个 SQL 查询,输出表中所有大国家的名称、人口和面积。

例如,根据上表,我们应该输出:

+--------------+-------------+--------------+
| name         | population  | area         |
+--------------+-------------+--------------+
| Afghanistan  | 25500100    | 652230       |
| Algeria      | 37100000    | 2381741      |
+--------------+-------------+--------------+

SELECT
    name, population, area
FROM
    world
WHERE
    area > 3000000

UNION

SELECT
    name, population, area
FROM
    world
WHERE
    population > 25000000

27.超过5名学生的课

-- 注意去重即可
select
class
from
courses
group by class
having count(DISTINCT student) >=5

28.好友申请1-总体通过率

-- 注意ifnull的用法以及理解题意
select
round(
ifnull(
(select count(*) from (select distinct requester_id, accepter_id from RequestAccepted) as A)
/
(select count(*) from (select distinct sender_id, send_to_id from FriendRequest) as B),
0)
, 2) as accept_rate;

29.图书馆的人流量

编写一个 SQL 查询以找出每行的人数大于或等于 100 且 id 连续的三行或更多行记录。

表:Stadium
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| id            | int     |
| visit_date    | date    |
| people        | int     |
+---------------+---------+
visit_date 是表的主键
每日人流量信息被记录在这三列信息中:序号 (id)、日期 (visit_date)、 人流量 (people)
每天只有一行记录,日期随着 id 的增加而增加
 
返回按 visit_date 升序排列的结果表。

查询结果格式如下所示。

Stadium table:
+------+------------+-----------+
| id   | visit_date | people    |
+------+------------+-----------+
| 1    | 2017-01-01 | 10        |
| 2    | 2017-01-02 | 109       |
| 3    | 2017-01-03 | 150       |
| 4    | 2017-01-04 | 99        |
| 5    | 2017-01-05 | 145       |
| 6    | 2017-01-06 | 1455      |
| 7    | 2017-01-07 | 199       |
| 8    | 2017-01-09 | 188       |
+------+------------+-----------+

Result table:
+------+------------+-----------+
| id   | visit_date | people    |
+------+------------+-----------+
| 5    | 2017-01-05 | 145       |
| 6    | 2017-01-06 | 1455      |
| 7    | 2017-01-07 | 199       |
| 8    | 2017-01-09 | 188       |
+------+------------+-----------+
id 为 5、6、7、8 的四行 id 连续,并且每行都有 >= 100 的人数记录。
请注意,即使第 7 行和第 8 行的 visit_date 不是连续的,输出也应当包含第 8 行,因为我们只需要考虑 id 连续的记录。
不输出 id 为 2 和 3 的行,因为至少需要三条 id 连续的记录。
-- 以前写过一遍 我在纠结select 和 having的执行顺序问题,怎么和hive的不一样嫩
//可直接子查询过滤出count(1) >3 的:where rk in (
//select rk from t1 group by rk having count(1) >= 3);
select 
id,
to_char(visit_date,'yyyy-mm-dd') as visit_date,
people
from 
(
select
id,
visit_date,
people,
count(1) over(partition by rn ) as rm1
from
(
select
id,
visit_date,
people,
(id - row_number() over(order by id)) as rn  
from
Stadium
where 
people >= 100
) t
) t1
where rm1 >=3
order by id 
select distinct t2.*    
from(
    select *, 
    lead(people,1)over(order by visit_date  ) as p2,
    lead(people,2)over(order by visit_date ) as p3
    from Stadium 
)t, Stadium t2
where t.people >=100 and p2>=100 and p3>=100 and 
t2.id>=t.id and t2.id-2<=t.id

30.好友申请2-谁有最多的好友(注意union all 用法)

表 request_accepted 存储了所有好友申请通过的数据记录,其中, requester_id 和 accepter_id 都是用户的编号。

 

| requester_id | accepter_id | accept_date|
|--------------|-------------|------------|
| 1            | 2           | 2016_06-03 |
| 1            | 3           | 2016-06-08 |
| 2            | 3           | 2016-06-08 |
| 3            | 4           | 2016-06-09 |
写一个查询语句,求出谁拥有最多的好友和他拥有的好友数目。对于上面的样例数据,结果为:

| id | num |
|----|-----|
| 3  | 3   |
注意:

保证拥有最多好友数目的只有 1 个人。
好友申请只会被接受一次,所以不会有 requester_id 和 accepter_id 值都相同的重复记录。

-- 开窗写不知道哪里错了,用spark检验一下,没检查出来重新了一遍,估计是符号错了
select
id,num
from
(
select 
id,sum(num) as num,
row_number(order by sum(num)  desc) as  rn
from 
(
select 
requester_id as id,
count(1) as num
from
request_accepted
group by requester_id
union all 
select 
accepter_id as id,
count(1) as num
from
request_accepted
group by accepter_id
) 
group by id
) t
where rn =1 
-- 正确的写法
select
id,nu as num
from
(
select
id,sum(num) as nu,
row_number() over(order by sum(num) desc) as rn
from
(
select requester_id as id, count(1) as num from request_accepted group by requester_id
union all
select  accepter_id as id1, count(1) as num1 from request_accepted group by accepter_id
) t1
group by id
) t2
where rn =1
-- 使用union all 连接
select id, count(*) num
from 
    (select requester_id id from request_accepted 
    union all
    select accepter_id id from request_accepted) tmp 
group by id
order by count(*) desc
limit 1;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,076评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,658评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,732评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,493评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,591评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,598评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,601评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,348评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,797评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,114评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,278评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,953评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,585评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,202评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,180评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,139评论 2 352

推荐阅读更多精彩内容