38 2016年的投资
astak16 opened this issue · comments
题目
将 2016 年 (TIV_2016) 所有成功投资的金额加起来,保留 2 位小数。
对于一个投保人,他在 2016 年成功投资的条件是:
- 他在 2015 年的投保额 (TIV_2015) 至少跟一个其他投保人在 2015 年的投保额相同。
- 他所在的城市必须与其他投保人都不同(也就是说维度和经度不能跟其他任何一个投保人完全相同)。
create table insurance (
pid int,
tiv_2015 float,
tiv_2016 float,
lat float,
lon float
);
insert into insurance (pid, tiv_2015, tiv_2016, lat, lon) values
('1', '10', '5', '10', '10'),
('2', '20', '20', '20', '20'),
('3', '10', '30', '20', '20'),
('4', '10', '40', '40', '40');
分析
这题的难点有两个:
- 一个人在 2015 年投保额至少和任何一个人在 2015年的投保额相同
- 筛选出不重复的地点
解决了这两个方法,这题就解决了。 方法一使用的是窗口函数,方法二使用的是子查询
SQL:方法一
select round(sum(tiv_2016), 2) tiv_2016 from (
select
tiv_2016,
count(*) over(partition by tiv_2015) count_tiv_2015,
count(*) over(partition by lat, lon) count_lat_lon
from insurance
) as temp where count_lat_lon = 1 and count_tiv_2015 > 1
解析
使用窗口函数进行分组
-
筛选出一个人在 2015 年投保额至少和任何一个人在 2015 年的投保额相同,就是按照 2015 年的投保额进行分组,并计算个数
count(*) over(partition by tiv_2015) count_tiv_2015
-
筛选不同的地点,就是按照经纬度进行分组,计算每个每个经纬度的个数
count(*) over(partition by lat, lon) count_lat_lon
-
将
1
、2
两步和tiv_2016
字段作为临时表temp
-
查询
temp
筛选出问题中的两步count_tiv_2015 > 1
投保额tiv_2015
至少有两个人是相等的count_lat_lon = 1
经纬度是唯一的
-
通过筛选条件后使用
sum()
计算出tiv_2016
并且使用round()
保留两个小数
SQL:方法二
select round(sum(tiv_2016), 2) tiv_2016 from insurance where tiv_2015 in (
select tiv_2015 from insurance group by tiv_2015 having count(*) > 1
) and concat(lat, lon) in (
select
concat(lat, lon)
from insurance group by lat, lon having count(*) = 1
);
解析
使用子查询
-
筛选出一个人在 2015 年投保额至少和任何一个人在 2015 年的投保额相同,使用
group by
按照tiv_2015
分组,并且使用count()
计算,筛选出大于1
的数据,因为大于1
代表至少有两个人在 2015 年投保额相同select tiv_2015 from insurance group by tiv_2015 having count(*) > 1;
-
筛选不同的地点,就是按照
lat
和lon
进行分组,这里查询的字段是使用concat()
连接lat, lon
后的值,并且使用count()
计算,筛选出等于1
的数据,因为等于1
代表地点唯一ps:使用
in
操作会消耗性能select concat(lat, lon) from insurance group by lat, lon having count(*) = 1;
-
查询
insurance
,筛选条件是两个子查询,tiv_2015 in 1
和concat(lat, lon) in 2
-
通过筛选条件后使用
sum()
计算出tiv_2016
并且使用round()
保留两个小数