LeetCode-585. 2016年的投资,exists

写一个查询语句,将 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

知识点:

关键字 on

数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户。

在使用 left jion 时,on 和 where 条件的区别如下:

  • 1、 on 条件是在生成临时表时使用的条件,它不管 on 中的条件是否为真,都会返回左边表中的记录。
  • 2、where 条件是在临时表生成好后,再对临时表进行过滤的条件。这时已经没有 left join 的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉。

exists

    指定一个子查询,检测行的存在。遍历循环外表,然后看外表中的记录有没有和内表的数据一样的。匹配上就将结果放入结果集中。

你可能感兴趣的:(语言,数据库)