☰
LeetCode 585题解析:SQL筛选2016年独特投资记录
2026/10/31 8:53:45 网站建设 项目流程

1. 题目背景与核心需求

这道LeetCode数据库题目编号585,标题为"2016年的投资",属于典型的商业数据分析类SQL应用题。题目模拟了一个保险公司的投资数据场景,要求我们筛选出2016年满足特定条件的投资记录。

在实际业务中,保险公司需要定期分析投资组合的表现,识别那些在特定年份同时满足"金额达标"和"地理位置独特"条件的优质投资。这类分析能帮助公司评估投资策略有效性,发现潜在的高回报区域或客户群体。

2. 数据模型理解与表结构分析

根据题目描述,我们主要处理两个表:

2.1 保险表(Insurance)

+-------------+-------+ | Column Name | Type | +-------------+-------+ | pid | int | -- 保单ID | tiv_2015 | float | -- 2015年投资金额 | tiv_2016 | float | -- 2016年投资金额 | lat | float | -- 纬度 | lon | float | -- 经度 +-------------+-------+

2.2 题目要求

编写SQL查询,找出满足以下两个条件的所有2016年投资记录:

  1. 该保单的2015年投资金额(tiv_2015)在至少一个其他保单中也存在(即有其他保单的2015年投资金额相同)
  2. 该保单的地理位置(lat, lon组合)在所有保单中是唯一的

最终输出2016年总投资金额,四舍五入到两位小数。

3. 解题思路拆解

3.1 条件一实现:相同2015年投资金额

我们需要找出那些tiv_2015值不唯一的记录。可以通过以下两种方式实现:

-- 方法1:使用窗口函数 SELECT pid, tiv_2015 FROM ( SELECT *, COUNT(*) OVER(PARTITION BY tiv_2015) as cnt FROM Insurance ) t WHERE cnt > 1 -- 方法2:使用子查询 SELECT pid, tiv_2015 FROM Insurance WHERE tiv_2015 IN ( SELECT tiv_2015 FROM Insurance GROUP BY tiv_2015 HAVING COUNT(*) > 1 )

3.2 条件二实现:唯一地理位置

确保(lat, lon)组合在全表唯一:

SELECT pid, lat, lon FROM ( SELECT *, COUNT(*) OVER(PARTITION BY lat, lon) as loc_cnt FROM Insurance ) t WHERE loc_cnt = 1

3.3 综合查询方案

将两个条件用INTERSECT连接:

SELECT ROUND(SUM(tiv_2016), 2) as tiv_2016 FROM ( -- 满足条件1的记录 SELECT pid, tiv_2016 FROM Insurance WHERE tiv_2015 IN ( SELECT tiv_2015 FROM Insurance GROUP BY tiv_2015 HAVING COUNT(*) > 1 ) INTERSECT -- 满足条件2的记录 SELECT pid, tiv_2016 FROM ( SELECT *, COUNT(*) OVER(PARTITION BY lat, lon) as loc_cnt FROM Insurance ) t WHERE loc_cnt = 1 ) result

4. 优化后的完整解决方案

经过多次测试和优化,最终的高效解决方案如下:

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(*) > 1 ) AND (lat, lon) IN ( SELECT lat, lon FROM Insurance GROUP BY lat, lon HAVING COUNT(*) = 1 )

这个版本比使用INTERSECT的方案更简洁,执行效率也更高。关键在于理解:

  1. 第一个子查询筛选出tiv_2015不唯一的记录
  2. 第二个子查询筛选出地理位置唯一的记录
  3. 两个条件用AND连接,同时满足的记录才会被选中

5. 执行计划分析与性能考量

5.1 索引建议

对于大型数据集,建议在以下列上创建索引:

  • tiv_2015:加速GROUP BY和IN子查询
  • (lat, lon):复合索引加速地理位置唯一性检查
CREATE INDEX idx_tiv2015 ON Insurance(tiv_2015); CREATE INDEX idx_location ON Insurance(lat, lon);

5.2 替代方案对比

方案优点缺点
窗口函数+INTERSECT逻辑清晰,易于理解需要多次扫描表,性能较差
子查询+AND连接执行效率高对复合条件理解要求较高
临时表方案适合超大数据集需要额外存储空间

6. 常见错误与调试技巧

6.1 典型错误示例

错误1:忘记四舍五入

-- 错误:直接SUM没有ROUND SELECT SUM(tiv_2016) AS tiv_2016

错误2:地理位置判断逻辑错误

-- 错误:单独判断lat和lon的唯一性 WHERE lat IN (...) AND lon IN (...)

6.2 调试建议

  1. 先分别验证两个条件的筛选结果是否正确
  2. 使用CTE(Common Table Expression)提高可读性:
WITH same_tiv2015 AS ( SELECT pid FROM Insurance WHERE tiv_2015 IN (...) ), unique_loc AS ( SELECT pid FROM (...) ) SELECT ROUND(SUM(tiv_2016), 2) FROM Insurance WHERE pid IN (SELECT pid FROM same_tiv2015) AND pid IN (SELECT pid FROM unique_loc)

7. 业务场景延伸

这道题目实际模拟了金融行业的以下业务场景:

  1. 异常交易检测:通过金额重复性和地理位置唯一性识别可疑交易
  2. 客户价值分析:找出既有历史交易(2015)又有新交易(2016)的独特客户
  3. 区域市场分析:识别那些在特定地理区域的独特投资行为

在实际工作中,类似的SQL技巧还可应用于:

  • 电商平台的重复订单检测
  • 物流系统中的异常路线识别
  • 社交网络的用户行为分析

8. 高级技巧与变体问题

8.1 使用EXISTS优化

对于某些数据库引擎,EXISTS可能比IN性能更好:

SELECT ROUND(SUM(tiv_2016), 2) AS tiv_2016 FROM Insurance i1 WHERE EXISTS ( SELECT 1 FROM Insurance i2 WHERE i1.pid != i2.pid AND i1.tiv_2015 = i2.tiv_2015 ) AND NOT EXISTS ( SELECT 1 FROM Insurance i3 WHERE i1.pid != i3.pid AND i1.lat = i3.lat AND i1.lon = i3.lon )

8.2 题目变体思考

如果题目改为"找出2016年投资金额比2015年增长超过10%且地理位置唯一的保单",解决方案:

SELECT ROUND(SUM(tiv_2016), 2) AS tiv_2016 FROM Insurance WHERE (lat, lon) IN ( SELECT lat, lon FROM Insurance GROUP BY lat, lon HAVING COUNT(*) = 1 ) AND tiv_2016 > tiv_2015 * 1.1

9. 不同数据库方言的适配

9.1 MySQL/MariaDB

SELECT ROUND(SUM(tiv_2016), 2) AS tiv_2016 FROM Insurance i WHERE EXISTS ( SELECT 1 FROM Insurance WHERE pid != i.pid AND tiv_2015 = i.tiv_2015 ) AND NOT EXISTS ( SELECT 1 FROM Insurance WHERE pid != i.pid AND lat = i.lat AND lon = i.lon )

9.2 PostgreSQL

可以利用PG特有的DISTINCT ON语法:

WITH dup_tiv AS ( SELECT tiv_2015 FROM Insurance GROUP BY tiv_2015 HAVING COUNT(*) > 1 ), unique_loc AS ( SELECT DISTINCT ON (lat, lon) pid FROM Insurance GROUP BY lat, lon, pid HAVING COUNT(*) = 1 ) SELECT ROUND(SUM(tiv_2016), 2) AS tiv_2016 FROM Insurance WHERE tiv_2015 IN (SELECT tiv_2015 FROM dup_tiv) AND pid IN (SELECT pid FROM unique_loc)

10. 学习路径建议

要系统掌握这类SQL问题,建议的学习路线:

  1. 基础语法:

    • SELECT, WHERE, GROUP BY, HAVING
    • 聚合函数(SUM, COUNT等)
    • JOIN操作(INNER, LEFT, RIGHT)
  2. 中级技巧:

    • 子查询和CTE
    • 窗口函数
    • 日期处理函数
  3. 高级主题:

    • 查询优化与执行计划
    • 索引策略
    • 分区表
  4. 实战平台:

    • LeetCode数据库题库
    • HackerRank SQL挑战
    • 公司真实业务场景

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询