LeetCode SQL 585. Investments in 2016【group by+having count;窗口函数】中等

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

Insurance 表:

sql 复制代码
+-------------+-------+
| Column Name | Type  |
+-------------+-------+
| pid         | int   |
| tiv_2015    | float |
| tiv_2016    | float |
| lat         | float |
| lon         | float |
+-------------+-------+
pid 是这张表的主键。
表中的每一行都包含一条保险信息,其中:
pid 是投保人的投保编号。
tiv_2015 是该投保人在 2015 年的总投保金额,tiv_2016 是该投保人在 2016 年的总投保金额。
lat 是投保人所在城市的纬度。题目数据确保 lat 不为空。
lon 是投保人所在城市的经度。题目数据确保 lon 不为空。

请你编写一个 SQL 查询,报告 2016 年 (tiv_2016) 所有满足下述条件的投保人的投保金额之和:

  • 他在 2015 年的投保额 (tiv_2015) 至少跟一个其他投保人在 2015 年的投保额相同。
  • 他所在的城市必须与其他投保人都不同(也就是说 (lat, lon) 不能跟其他任何一个投保人完全相同)。

tiv_2016 四舍五入的 两位小数

查询结果格式如下例所示。

示例:

sql 复制代码
输入:
Insurance 表:
+-----+----------+----------+-----+-----+
| 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    |
+----------+
解释:
表中的第一条记录和最后一条记录都满足两个条件。
tiv_2015 值为 10 与第三条和第四条记录相同,且其位置是唯一的。
第二条记录不符合任何一个条件。其 tiv_2015 与其他投保人不同,并且位置与第三条记录相同,这也导致了第三条记录不符合题目要求。
因此,结果是第一条记录和最后一条记录的 tiv_2016 之和,即 45 。

解法1 子查询

本题的要求比较多,对每个投保人,先检查:

  • 他在 2015 年的投保额 (tiv_2015) 至少跟一个 其他投保人在 2015 年的投保额相同
  • 他所在的城市必须与其他投保人都不同 (也就是说 (lat, lon) 不能跟其他任何一个投保人完全相同)。

然后报告 2016 年 (tiv_2016) 所有满足这两个条件的投保人的投保金额之和。

要判断一个人 x x x 的投保额是否和其他投保人(至少一个)相同,有几种办法。第一是:子查询中,筛选出不等于 x . p i d {x.pid} x.pid 的其他人在 2015 的投保额,然后判断 x x x 的投保额 t i v _ 2015 tiv\_2015 tiv_2015 是否在这些投保额中

sql 复制代码
select ...
from Insurance as A
where tiv_2015 in (
    select tiv_2015 from Insurance as B
    where A.pid != B.pid
)

另外一种是,子查询中使用 group byhaving count ,筛选出那些出现次数超过 1 1 1 的投保额。

sql 复制代码
select ...
from Insurance as A
where tiv_2015 in (
    select tiv_2015 from Insurance as B
    group by tiv_2015
    having count(*) > 1
)

这两种「检查至少跟其他一个相同」的写法,还能用来判断「都不同」。不过这里,我们需要连接 LATLON 为一个整体,以表示坐标信息

注意:这两条要求需要不分顺序同时满足,所以如果想要先用 LAT 来筛选一遍数据,然后再用 LON 来筛选,会得到错误的结果。

sql 复制代码
(lat, lon) not in (
    select lat, lon from Insurance as B
    where A.pid != B.pid
)

-- 或者写成
concat(lat, lon) in (
    select concat(lat, lon) from Insurance
    group by lat, lon
    having count(*) = 1
)

答案如下:

mysql 复制代码
select round(sum(tiv_2016), 2) as tiv_2016
from Insurance as A
where tiv_2015 in (
    select tiv_2015 from Insurance as B
    where A.pid != B.pid
) and (lat, lon) not in (
    select lat, lon from Insurance as B
    where A.pid != B.pid
);

select round(sum(tiv_2016), 2) as tiv_2016
from Insurance as A
where A.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
);

解法2 窗口函数

tiv_2015(lat, lon) 分别使用窗口函数,对它们先分组 partition 然后计算每组的数量:

mysql 复制代码
select round(sum(tiv_2016), 2) as tiv_2016
from (
    select tiv_2016,
        count(1) over (partition by tiv_2015) as tiv_2015_count,
        count(1) over (partition by lat, lon) as location_count
    from Insurance
) tmp
where tiv_2015_count <> 1 and location_count = 1
相关推荐
watermelonoops3 分钟前
Deepin和Windows传文件(Xftp,WinSCP)
linux·ssh·deepin·winscp·xftp
Hacker_LaoYi36 分钟前
SQL注入的那些面试题总结
数据库·sql
疯狂飙车的蜗牛1 小时前
从零玩转CanMV-K230(4)-小核Linux驱动开发参考
linux·运维·驱动开发
Hacker_LaoYi3 小时前
【渗透技术总结】SQL手工注入总结
数据库·sql
独行soc3 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍06-基于子查询的SQL注入(Subquery-Based SQL Injection)
数据库·sql·安全·web安全·漏洞挖掘·hw
远游客07133 小时前
centos stream 8下载安装遇到的坑
linux·服务器·centos
马甲是掉不了一点的<.<3 小时前
本地电脑使用命令行上传文件至远程服务器
linux·scp·cmd·远程文件上传
jingyu飞鸟3 小时前
centos-stream9系统安装docker
linux·docker·centos
超爱吃士力架4 小时前
邀请逻辑
java·linux·后端
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论