MySQL查询中为姓名年度首次出现标记1,其余标记0
如何在SQL中标记姓名每年的首次出现?
我明白你的需求——想要给每个姓名在对应年份里的第一条记录标记1,其余标记0,而且之前拆分查询关联的方式还导致了重复数据,确实挺头疼的。其实这个需求用窗口函数就能完美解决,不用绕弯路。
先回顾下你的原始表tbl_name:
| ID | Name | Month | Quarter | Year |
|---|---|---|---|---|
| 1 | A | Jan | 1 | 2019 |
| 1 | A | Feb | 1 | 2019 |
| 2 | B | May | 2 | 2019 |
| 3 | C | May | 2 | 2018 |
你的目标是生成带Unique Count列的结果集,接下来看具体实现:
核心解决方案:用ROW_NUMBER()窗口函数
窗口函数可以在不拆分表的前提下,对分组后的记录进行排序和标记,完全避免重复数据的问题。
支持标准窗口函数的数据库(MySQL 8.0+/PostgreSQL/SQL Server/Oracle)
这些数据库直接用下面的SQL语句即可:
SELECT ID, Name, Month, Quarter, Year, CASE WHEN ROW_NUMBER() OVER (PARTITION BY Name, Year ORDER BY Month) = 1 THEN 1 ELSE 0 END AS `Unique Count` FROM tbl_name;
PARTITION BY Name, Year:把数据按「姓名+年份」分组,确保我们只在同一个人同一年的记录里判断首次出现。ORDER BY Month:给每组内的记录按月份排序,这样第一个月份的记录就是该姓名当年的首次出现,标记为1。如果你的月份是字符串(比如Jan、Feb),大部分数据库会按字典序正常排序,和实际月份顺序一致;如果是数字格式的月份,排序更准确。
如果你更信任ID的自增顺序来判断首次出现,也可以把排序字段换成ID:
SELECT ID, Name, Month, Quarter, Year, CASE WHEN ROW_NUMBER() OVER (PARTITION BY Name, Year ORDER BY ID) = 1 THEN 1 ELSE 0 END AS `Unique Count` FROM tbl_name;
旧版本MySQL(5.x及以下,不支持窗口函数)
如果你的MySQL版本比较老,不支持窗口函数,可以用子查询来实现:
SELECT t1.ID, t1.Name, t1.Month, t1.Quarter, t1.Year, CASE WHEN t1.ID = (SELECT MIN(t2.ID) FROM tbl_name t2 WHERE t2.Name = t1.Name AND t2.Year = t1.Year) THEN 1 ELSE 0 END AS `Unique Count` FROM tbl_name t1;
这个逻辑是通过子查询找到每个「姓名+年份」组合下的最小ID(也就是最早插入的记录),匹配上的标记为1,其他为0。
为什么之前的关联方式会出问题?
你之前拆分查询关联导致重复,大概率是因为去重后的表和原表关联时,一条去重记录对应多条原表记录,数据库会把这些记录全部返回,自然就产生了重复。而窗口函数是在原表的每条记录上直接计算标记,不会引入额外的重复数据。
内容的提问来源于stack exchange,提问作者wayneloo
相关产品推荐
相关产品推荐

