写一个查询语句,将 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 |
解释
就如最后一个投保人,第一个投保人同时满足两个条件:
1. 他在 2015 年的投保金额 TIV_2015 为 '10' ,与第三个和第四个投保人在 2015 年的投保金额相同。
2. 他所在城市的经纬度是独一无二的。
第二个投保人两个条件都不满足。他在 2015 年的投资 TIV_2015 与其他任何投保人都不相同。
且他所在城市的经纬度与第三个投保人相同。基于同样的原因,第三个投保人投资失败。
所以返回的结果是第一个投保人和最后一个投保人的 TIV_2016 之和,结果是 45 。
题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/investments-in-2016
审题:2015年的投保金额至少与一个人相同,所在城市和别人不同(经纬度都不同)
思考:连接两个表,查找是否有相同2015年的投保,然后在对比是否城市不同。
解题:
采用排除法。
对每个投保人A,如果同城存在另一个人B,则A和B都要排除。
SQL代码:
-- exists 指定一个子查询,检测行的存在。遍历循环外表,然后看外表中的记录有没有和内表的数据一样的。-匹配上就将结果放入结果集中。
-- 查询投保编号不同,城市相同的人,需要都排除
exists(
select *
from insurance as B
where B.PID != A.PID and B.LAT = A.LAT and B.LON = A.LON
)
如果没有一个人B,与A在2015的投保额相同,则A要排除。
-- 还需要有一个投保人投的和自己相同。
exists(
select *
from insurance as B
where B.PID != A.PID and B.TIV_2015 = A.TIV_2015
)
合起来排除掉无用的数据,再对2016的投保额求和。
select sum(A.TIV_2016) as `TIV_2016`
from insurance as A
where exists(
select *
from insurance as B
where B.PID != A.PID and B.TIV_2015 = A.TIV_2015
) and not exists(
select *
from insurance as B
where B.PID != A.PID and B.LAT = A.LAT and B.LON = A.LON
)
WHERE子句可以与AND,OR和NOT运算符结合使用。
and 表示 查询的语句必须全部包含and 连接的两个或多个条件
or 表示 查询的语句包含or连接条件中的其中一个
not 表示 查询的语句中不包含not连接的条件
分三步解决。
第一步,找出所有2015投保额相同的人。
结果命名为表A。
(
SELECT DISTINCT A.*
FROM
insurance AS A
JOIN insurance AS B ON (B.PID != A.PID AND B.TIV_2015 = A.TIV_2015)
) AS A
第二步,找出所有在同一个城市的人。
结果命名为表B。
(
SELECT DISTINCT A.pid
FROM
insurance AS A
JOIN insurance AS B ON (B.PID != A.PID AND B.LAT = A.LAT AND B.LON = A.LON)
) AS B
第三步,从表A中排除掉表B中的id。
典型的集合差,用left join连接表A和表B。
再求和。
SELECT sum(A.TIV_2016) as `TIV_2016`
FROM
(
SELECT DISTINCT A.*
FROM
insurance AS A
JOIN insurance AS B ON (B.PID != A.PID AND B.TIV_2015 = A.TIV_2015)
) AS A
LEFT JOIN
(
SELECT DISTINCT A.pid
FROM
insurance AS A
JOIN insurance AS B ON (B.PID != A.PID AND B.LAT = A.LAT AND B.LON = A.LON)
) AS B
ON (A.pid = B.pid)
WHERE B.pid IS NULL
知识点:
数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户。
在使用 left jion 时,on 和 where 条件的区别如下:
指定一个子查询,检测行的存在。遍历循环外表,然后看外表中的记录有没有和内表的数据一样的。匹配上就将结果放入结果集中。