sqlserver查询数据表中每个类别最新的一条记录

本文介绍如何在SQLServer中查询数据表tariff_info,按code分类获取每类最新的记录,并添加时间过滤条件,确保start_time和end_time之间的时间区间正确对比,避免临界日期问题。

表tariff_info, 原始数据:

 

想要的结果:以start_time时间倒序排序, 以code分类, 查询每一类最新的一条记录

sql:

SELECT
    a.*
FROM
    TARIFF_INFO a,
    (
        SELECT
            code,
            MAX (start_time) start_time
        FROM
            TARIFF_INFO
        GROUP BY
            code
    ) b
WHERE
    a.start_time = b.start_time
AND a.code = b.code
ORDER BY
    a.code

 这样查询结果已经实现了, 但是需要添加过滤条件, 即当前时间在start_time和end_time之间.  如果还用上面的sql仅仅在末尾追加一个条件发现查询结果为空:

所以必须在子查询中添加同样的过滤条件方可查出正确结果, 如下面sql:

SELECT
    a.*
FROM
    TARIFF_INFO a,
    (
        SELECT
            code,
            MAX (start_time) start_time
        FROM
            TARIFF_INFO
        WHERE
            start_time <= '2018-09-18'
        GROUP BY
            code
    ) b
WHERE
    a.start_time = b.start_time
AND a.code = b.code
AND a.start_time <= '2018-09-18'
ORDER BY
    a.code

 

另:需要注意sql中时间区间的比较<=貌似有临界日期会漏掉的情况, 这里不作赘述了.以下sql和上面的异曲同工:

SELECT
    a.*
FROM
    TARIFF_INFO a
INNER JOIN (
    SELECT
        code,
        MAX (start_time) 'start_time'
    FROM
        TARIFF_INFO
    WHERE CONVERT(VARCHAR(10),start_time,121) <= '2018-09-18'
    GROUP BY
        code
) b ON a.code = b.code
AND a.start_time = b.start_time 
AND CONVERT(VARCHAR(10),b.start_time,121) <= '2018-09-18'
ORDER BY
    a.code

 

感谢:https://blog.youkuaiyun.com/sinat_27693393/article/details/78673971

 

<think>我们有一个出勤记录表,假设表名为 attendance,包含以下字段: student_id (学生ID) attendance_time (出勤时间) ... 其他字段 我们需要为每个学生查询最新一条出勤记录(即每个学生出勤时间最大的一条记录)。 方法1:使用子查询,先找到每个学生最大的出勤时间,然后根据学生ID和最大出勤时间关联回原表。 方法2:使用窗口函数,按每个学生分区,按出勤时间降序排序,然后取每个分区的第一行。 由于用户要求每个学生最新一条,这里我们使用窗口函数的方法更直接。 假设表结构如下(根据引用[2]和引用[4]中的类似场景,我们假设有学生ID和出勤时间): attendance 表: student_id: 学生ID attendance_date: 出勤日期(或者出勤时间,我们假设是日期时间类型) 我们使用ROW_NUMBER()来为每个学生的记录按出勤时间降序排序,然后取排序为1的记录。 SQL语句如下: ```sql SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY student_id ORDER BY attendance_date DESC) AS rn FROM attendance ) t WHERE rn = 1; ``` 解释: - 在子查询中,我们使用窗口函数 ROW_NUMBER() 对每个学生(按student_id分区)的出勤记录按出勤日期降序(DESC)排序,这样最近的一次出勤记录就会排在第一,编号为1。 - 外层查询选择所有排序号为1的记录,即每个学生的最新出勤记录。 注意:如果同一个学生同一时间有多次记录(比如精确到秒的时间戳不同),那么会按具体时间排序,取时间最大的那一条。如果同一时间有多个记录(比如只记录到天),那么可能会随机取一条(因为同一时间排序相同,但ROW_NUMBER()会给不同的序号),此时可以考虑使用RANK()或DENSE_RANK(),但这里我们只需要一条,所以ROW_NUMBER()是合适的。 另外,如果用户需要的是每天的最新记录,但一天内可能有多次,那么上述方法会取一天内时间最大的那一次。如果用户只需要每个学生最近一次出勤的日期(不管一天内多次),那么可以只按日期排序(如果attendance_date是日期类型,则不会有时间部分)。 根据引用[4]中处理连续日期的思路,我们这里也是用到了窗口函数,但需求不同。 如果用户的需求是每个学生最新一条记录(可能包含多个字段),那么上述查询可以满足。 如果表中有其他需要显示的字段,都可以在子查询中选取。 注意:如果出勤记录数据量很大,可以考虑在(student_id, attendance_date)上建立索引,以提高查询效率。 根据引用[3]中提到的分组和排序,这里我们使用了窗口函数进行分组(分区)和排序。 因此,最终SQL语句如上。 如果用户使用的数据库不支持窗口函数(如MySQL 8.0以下版本),则可以使用以下方法: 方法:使用子查询先获取每个学生的最晚出勤时间,然后与原表关联。 ```sql SELECT a.* FROM attendance a JOIN ( SELECT student_id, MAX(attendance_date) AS max_date FROM attendance GROUP BY student_id ) b ON a.student_id = b.student_id AND a.attendance_date = b.max_date; ``` 但是这种方法有一个问题:如果同一个学生同一个时间点有两条记录,那么这两条都会出现。如果希望只取一条,可能需要另外的字段来排序(比如主键),或者使用其他方法去重。因此,窗口函数的方法更精确。 考虑到现在大多数数据库都支持窗口函数(MySQL 8.0+,PostgreSQL,SQL Server,Oracle等),推荐使用窗口函数的方法。 如果用户使用的是不支持窗口函数的MySQL旧版本,并且可能出现同一时间多条记录的情况,我们可以使用以下方法: ```sql SELECT a.* FROM attendance a LEFT JOIN attendance a2 ON a.student_id = a2.student_id AND a.attendance_date < a2.attendance_date WHERE a2.student_id IS NULL; ``` 解释:通过左连接,找到那些没有比当前记录更晚的出勤记录(即当前记录最新的)。但是这种方法在数据量大时效率较低。 综上所述,优先推荐窗口函数的方法。 根据用户的问题,我们只要求最新一条记录,所以使用窗口函数ROW_NUMBER()是合适的。 如果用户需要的是每个学生最新的出勤记录一条),那么使用第一个窗口函数的SQL即可。 注意:在引用[2]中提到了出勤率的计算,但这里用户的需求是查询最新出勤记录,所以不需要聚合。 请用户根据自己数据库的支持情况选择合适的方法。 如果用户需要的是其他字段,只需在SELECT子句中指定即可。 因此,我们给出两种主要方法: 1. 窗口函数(推荐) 2. 自连接或子查询(适用于不支持窗口函数的数据库) 最终,我们给出窗口函数的写法。 如果用户需要的是出勤记录表中每个学生的最新记录,那么SQL如上。 如果用户有多个表需要连接,比如还需要学生信息,那么可以在上述查询的基础上进行连接。但用户问题只要求每个学生的最新出勤记录,所以先按上述方法获取出勤记录,再连接学生表即可。 例如,假设还有学生表students(student_id, student_name, ...),那么可以: ```sql SELECT s.student_id, s.student_name, t.* FROM ( -- 上面的子查询,获取每个学生的最新出勤记录 SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY student_id ORDER BY attendance_date DESC) AS rn FROM attendance ) a WHERE rn = 1 ) t JOIN students s ON t.student_id = s.student_id; ``` 这样就能显示学生信息以及最新的出勤记录。 但用户问题中并没有提到需要连接其他表,所以只给出核心的查询出勤记录的SQL。 因此,针对用户问题的核心SQL是: ```sql SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY student_id ORDER BY attendance_date DESC) AS rn FROM attendance ) t WHERE rn = 1; ``` 请用户根据实际情况调整表名和字段名。</think>### SQL 查询每个学生最新一条出勤记录 #### 核心解决方案 ```sql SELECT * FROM ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY student_id ORDER BY attendance_date DESC ) AS rnk FROM attendance_records ) t WHERE rnk = 1 ``` #### 关键解析: 1. **窗口函数** `ROW_NUMBER() OVER(PARTITION BY student_id ORDER BY attendance_date DESC)` 实现: - `PARTITION BY student_id`:按学生分组 - `ORDER BY attendance_date DESC`:组内按出勤日期降序排序 - 为每条记录生成序号(最近记录=1) 2. **子查询过滤** 外层查询通过 `WHERE rnk = 1` 筛选每个学生的最新记录 #### 备选方案(无窗口函数时) ```sql SELECT a.* FROM attendance_records a INNER JOIN ( SELECT student_id, MAX(attendance_date) AS latest_date FROM attendance_records GROUP BY student_id ) b ON a.student_id = b.student_id AND a.attendance_date = b.latest_date ``` #### 注意事项: 1. **时间精度问题** 若出勤时间精确到秒,需用 `MAX(attendance_timestamp)` 替代日期 2. **并列记录处理** 若同一学生同一时间有多条记录: ```sql RANK() OVER(...) -- 并列记录会获得相同排名 WHERE rnk = 1 -- 会返回所有并列记录 ``` 3. **索引优化建议** 在 `(student_id, attendance_date)` 建立复合索引可加速查询 #### 示例数据验证: | student_id | attendance_date | 其他字段 | |------------|-----------------|----------| | S001 | 2023-10-01 | ... | | S001 | 2023-10-05 | ... | ← 最新 | S002 | 2023-10-02 | ... | ← 最新 查询结果仅包含每个学生日期最大的记录(S001的10月5日,S002的10月2日)
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值