MSSQL ROW_NUMBER() OVER (...)实现数据的分组、排序、过滤重复、取Top n记录

本文介绍了如何利用MSSQL的ROW_NUMBER() OVER()函数进行数据分组、排序,以及过滤重复和选取Top n记录。通过partition by实现类似group by的功能,解决当某月份没有进展状态录入时,查询该月份之前最新进展的问题。通过添加行号rowNum,可以方便地进行数据筛选,避免完全重复的[CriterionID],[MissionIsFinish],[MissionMonth]记录。" 105435252,9462444,2017年百度杯CTF比赛爆破解析1-3,"['网络安全', 'CTF挑战', '漏洞利用', 'Web安全', 'Python编程']
select  [CriterionID],[MissionIsFinish] ,[MissionMonth] from 
(
select [CriterionID],[MissionIsFinish],[MissionMonth],
row_number() over(partition by [CriterionID] order by [MissionMonth] desc) as rowNum
from [fts].[v_TTM_DOAM_MissionList]
where [IsDel]=0 and [MissionYear]=2013 and [MissionMonth]<=12 and [MissionMonth]>0
group by [CriterionID],[MissionIsFinish],[MissionMonth]
) as tb
where rowNum=1

 

[fts].[v_TTM_DOAM_MissionList]列出了过往各工作内容(CriterionID)每个已录入月份(MissionMonth)的进展状态(MissionIsFinish),其中可能有的进展已经在过去的时间已经完成,后续不再录入,或者有的进展录入中间隔了n个月没录。

上面的代码是查询各进展最新月份(top 1)的记录, 如果要取top n,把最后的where 条件改为rowNum<=n即可

partition by 是用来分组的,可以理解为group by

该语句要实现的功能是:

查询工作内容(CriterionID)在当前月份(MissionMonth)的工作进展状态(MissionIsFinish)

问题在于:

如果我们要取工作内容某月份的进展状态,但是在该月份没有录入,则要取该月份之前的最新(最晚)的进展状态


结果为:

<think>在Java中,执行SQL查询并获排序后的第一条数据,通常可以通过在SQL语句中使用`ORDER BY`和`LIMIT`子句(或类似功能,决于数据库系统)来实现。但是,不同的数据库系统可能有不同的语法。 例如: - 在MySQL中,使用`ORDER BY ... LIMIT 1` - 在Oracle中,可以使用`ORDER BY ... FETCH FIRST 1 ROW ONLY`(12c及以上版本)或者使用子查询和`ROWNUM` - 在SQL Server中,使用`TOP 1 ... ORDER BY` - 在PostgreSQL中,与MySQL类似,使用`ORDER BY ... LIMIT 1` 因此,在编写代码时,需要根据所使用的数据库类型来构建相应的SQL语句。 下面以几种常见的数据库为例,展示如何实现: ### 1. MySQL / PostgreSQL ```sql SELECT column1, column2, ... FROM table_name ORDER BY sort_column DESC LIMIT 1; ``` ### 2. Oracle (12c及以上) ```sql SELECT column1, column2, ... FROM table_name ORDER BY sort_column DESC FETCH FIRST 1 ROW ONLY; ``` ### 3. Oracle (11g及以下) ```sql SELECT * FROM ( SELECT column1, column2, ... FROM table_name ORDER BY sort_column DESC ) WHERE ROWNUM = 1; ``` ### 4. SQL Server ```sql SELECT TOP 1 column1, column2, ... FROM table_name ORDER BY sort_column DESC; ``` 在Java代码中,我们可以使用JDBC来执行这样的SQL查询。以下是一个使用JDBC的示例,假设我们使用的是MySQL数据库: ```java import java.sql.*; public class GetFirstSortedRecord { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/your_database"; String user = "username"; String password = "password"; // 假设我们要从employees表中根据salary降序排序第一条记录 String sql = "SELECT id, name, salary FROM employees ORDER BY salary DESC LIMIT 1"; try (Connection conn = DriverManager.getConnection(url, user, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { if (rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); double salary = rs.getDouble("salary"); System.out.println("ID: " + id + ", Name: " + name + ", Salary: " + salary); } else { System.out.println("No records found."); } } catch (SQLException e) { e.printStackTrace(); } } } ``` ### 解释: 1. 首先,我们建立数据库连接(需要替换url、user和password为实际的值)。 2. 定义SQL语句:使用`ORDER BY`对salary进行降序排序,然后使用`LIMIT 1`获第一条记录。 3. 使用`Statement`对象执行查询,并获`ResultSet`。 4. 由于我们只期望一条记录,使用`rs.next()`一次即可。如果存在记录,则从结果集中获数据。 5. 在catch块中处理可能的SQL异常。 ### 注意: - 如果使用其他数据库,需要替换相应的JDBC驱动和SQL语句。 - 确保在项目中包含了对应数据库的JDBC驱动依赖。 ### 使用PreparedStatement(如果需要动态排序) 如果排序字段或顺序是动态的,为了避免SQL注入,建议使用`PreparedStatement`。但是,注意`ORDER BY`子句通常不能使用参数占位符(?)直接传递列名或排序方式(ASC/DESC),因为占位符只能用于值,不能用于列名或关键字。因此,在构建SQL语句时,需要确保动态部分的安全性,例如通过白名单验证。 示例(动态排序): ```java String sortColumn = "salary"; // 假设从安全来源获 String sortOrder = "DESC"; // 同样需要确保安全 // 注意:这里直接拼接了列名和排序顺序,需要确保它们的安全性(例如,通过白名单检查) String sql = "SELECT id, name, salary FROM employees ORDER BY " + sortColumn + " " + sortOrder + " LIMIT 1"; ``` 或者,使用不同的策略来构建安全的动态SQL。 ### 相关问题: §§相关问题§§: 1. 在Java中如何防止SQL注入? 2. 如果数据库表数据量很大,使用排序第一条数据是否会影响性能?如何优化? 3. 除了使用SQL语句的LIMIT(或类似)子句,在Java中还有其他方式获排序后的第一条数据吗? 4. 在JDBC中如何处理多个排序字段的情况?</think>在Java中实现SQL查询排序第一条数据的逻辑,主要决于使用的数据库类型。以下是不同数据库的实现方式及Java代码示例: ### 一、通用实现方案(推荐) 使用`ORDER BY`配合数据库特定的限制行数语法: ```java import java.sql.*; public class SqlFirstRecord { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/your_db"; String user = "username"; String password = "password"; // 根据数据库类型选择SQL语句 String sql = ""; String dbType = "mysql"; // 根据实际数据库修改 switch(dbType.toLowerCase()) { case "mysql": case "postgresql": sql = "SELECT * FROM employees ORDER BY salary DESC LIMIT 1"; break; case "oracle": sql = "SELECT * FROM (SELECT * FROM employees ORDER BY salary DESC) WHERE ROWNUM = 1"; break; case "sqlserver": sql = "SELECT TOP 1 * FROM employees ORDER BY salary DESC"; break; } try (Connection conn = DriverManager.getConnection(url, user, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { if (rs.next()) { System.out.println("Highest salary employee: " + rs.getString("name") + " - $" + rs.getDouble("salary")); } else { System.out.println("No records found"); } } catch (SQLException e) { e.printStackTrace(); } } } ``` ### 二、数据库特定语法详解 1. **MySQL/PostgreSQL/SQLite** ```sql SELECT * FROM table ORDER BY sort_column DESC LIMIT 1 ``` 2. **Oracle** ```sql SELECT * FROM ( SELECT * FROM table ORDER BY sort_column DESC ) WHERE ROWNUM = 1 ``` 3. **SQL Server** ```sql SELECT TOP 1 * FROM table ORDER BY sort_column DESC ``` ### 三、使用ROW_NUMBER()的通用写法(适用于所有支持窗口函数的数据库) ```java String sql = "SELECT * FROM (" + " SELECT *, ROW_NUMBER() OVER (ORDER BY salary DESC) as rn " + " FROM employees" + ") WHERE rn = 1"; ``` ### 四、PreparedStatement动态参数示例 ```java public Employee getTopEmployee(String sortColumn, boolean ascending) throws SQLException { String direction = ascending ? "ASC" : "DESC"; String sql = "SELECT * FROM employees ORDER BY " + sortColumn + " " + direction + " LIMIT 1"; try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { ResultSet rs = pstmt.executeQuery(); if (rs.next()) { return new Employee( rs.getInt("id"), rs.getString("name"), rs.getDouble("salary") ); } } return null; } ``` ### 关键注意事项 1. **性能优化**:在排序列上建立索引可大幅提升查询速度 2. **空结果处理**:始终检查`ResultSet.next()`的返回值 3. **SQL注入防护**:排序字段名不能使用PreparedStatement参数化,需通过白名单验证 ```java // 安全验证示例 Set<String> validColumns = Set.of("salary", "hire_date"); if (!validColumns.contains(sortColumn)) { throw new IllegalArgumentException("Invalid sort column"); } ``` ### 不同数据库的JDBC驱动依赖 | 数据库 | Maven依赖 | |-----------|-----------| | MySQL | `mysql:mysql-connector-java` | | PostgreSQL| `org.postgresql:postgresql` | | Oracle | `com.oracle.database.jdbc:ojdbc11` | | SQL Server| `com.microsoft.sqlserver:mssql-jdbc` |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值