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 核心查询条件
需要满足两个条件:
- 该投保人在2015年的投资金额(tiv_2015)与至少一个其他投保人相同
- 该投保人所在位置(lat, lon)与其他所有投保人都不同
3. 解决方案设计
3.1 查询思路拆解
要实现这个查询,需要分三步处理:
- 筛选2015年投资金额不唯一的记录
- 确保地理位置唯一的记录
- 对符合条件的记录计算2016年投资总额
3.2 SQL实现方案
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 )4. 关键技术点解析
4.1 子查询的运用
这里使用了两个子查询:
- 第一个子查询找出2015年投资金额出现多次的值
- 第二个子查询找出只出现一次的地理位置组合
注意:MySQL中可以直接使用元组(lat, lon)进行IN判断,但在某些数据库如SQL Server中可能需要改用CONCAT或单独判断
4.2 聚合函数与HAVING
HAVING子句用于对GROUP BY后的结果进行过滤:
COUNT(*) > 1找出重复值COUNT(*) = 1找出唯一值
4.3 精度处理
使用ROUND函数将结果保留两位小数,符合金融数据的常规显示要求。
5. 性能优化建议
5.1 索引设计
为提高查询效率,建议在以下列上创建索引:
CREATE INDEX idx_tiv_2015 ON Insurance(tiv_2015); CREATE INDEX idx_location ON Insurance(lat, lon);5.2 替代写法
使用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 )6. 常见错误与排查
6.1 错误示例1:忽略唯一性条件
-- 错误:只考虑了tiv_2015重复,没考虑位置唯一 SELECT SUM(tiv_2016) FROM Insurance WHERE tiv_2015 IN (...)6.2 错误示例2:错误的地理位置判断
-- 错误:分开判断lat和lon的唯一性 WHERE lat IN (...) AND lon IN (...) -- 正确:应该作为组合判断 WHERE (lat, lon) IN (...)6.3 错误示例3:精度问题
-- 错误:未处理小数位数 SELECT SUM(tiv_2016) AS tiv_2016 -- 正确:应该四舍五入 SELECT ROUND(SUM(tiv_2016), 2) AS tiv_20167. 业务场景扩展
这种查询模式在实际业务中应用广泛:
- 识别"在同一地区有多笔投资的客户"(将唯一条件改为非唯一)
- 找出"投资金额相同但地区分散的保单"(调整条件逻辑)
- 分析"特定年份投资金额的变化情况"(修改时间条件)
8. 学习建议
对于想系统提升SQL能力的学习者,建议:
- 从基础语法开始,掌握SELECT/WHERE/GROUP BY等核心语句
- 重点学习子查询、JOIN等高级用法
- 多做LeetCode数据库题目,按难度循序渐进
- 在实际业务场景中思考SQL的应用
这道题作为中等难度题目,很好地考察了:
- 多条件过滤的逻辑组合能力
- 子查询的灵活运用
- 对数据唯一性的理解
- 聚合函数的使用技巧
我在实际工作中发现,很多初级数据分析师最容易犯错的就是对唯一性条件的理解。建议在写复杂SQL时,先用注释写明每个条件的业务含义,再转化为SQL语法,这样可以减少逻辑错误。