Bootstrap

[点赞排行榜]关于IN字段不按照顺序返回问题

问题背景

在实现点赞排行榜功能当中,需要展示出最先点赞的五个人的基础信息,形成排行榜。

需求分析

我们需要选择合适的数据类型来存放点赞人的ID,需要保证:
点赞人的ID是唯一的,并且能够根据时间戳来排序,在List,Set,Zset能够满足这两种需求的只有Zset。
在存放到Zset中时,以笔记ID作为key,点赞人的ID作为value。
三种数据类型比较:
在这里插入图片描述

存在的Bug

通过测试得到,Redis中存放的点赞人ID是没问题的,但是到数据库查询时返回的结果无法按照zset给的ID顺序返回,原来的SQL语句:

select * from tb_user where id in (5,2,3)

原始代码

    @Override
    public Result queryBogLikes(Long blogId) {
        String key = BLOG_LIKE_PREFIX + blogId;
        Set<String> userIds = stringRedisTemplate.opsForZSet().range(key, 0, 5);
        //增加空指针判断
        if (userIds == null || userIds.isEmpty()) return Result.ok();
        //将ids到数据库查询
        List<UserDTO> userDTOList = userService.query()
                .in("id", userIds)
                .list()
                .stream()
                .map(user -> BeanUtil.copyProperties(user,UserDTO.class))
                .collect(Collectors.toList());

        return Result.ok(userDTOList);
    }

问题分析

:实际上,DBMS会按照其内部的优化策略或索引来决定如何检索和返回数据,这通常会导致结果按照与IN子句中指定的顺序不同的顺序返回。在某些情况下,DBMS甚至可能为了性能考虑而重新排序结果集,以确保最有效的数据检索和传输。
所以在查询的时候需要进行order by。

解决方案

无论因为何种问题,导致我们无法按照有规则的字段进行排序返回结果,只能通过in中顺序来返回的结果,我们只能使用自定义排序,MySQL中ORDER BY的field()函数,可以用来对SQL中查询结果集进行指定顺序排序。其一般语法为:field(字段,value,value,…)

1, 使用工具类将待查询ids拼接成以,间隔的字符串
2, 拼接SQL,按照field指定的ID顺序来展示结果集

    public Result queryBogLikes(Long blogId) {
        String key = BLOG_LIKE_PREFIX + blogId;
        Set<String> userIds = stringRedisTemplate.opsForZSet().range(key, 0, 5);
        //增加空指针判断
        if (userIds == null || userIds.isEmpty()) return Result.ok();
        String idStr = StrUtil.join(",", userIds);
        //将ids到数据库查询
        List<UserDTO> userDTOList = userService.query()
                .in("id", userIds)
                .last("order by field(id," + idStr + ")")
                .list()
                .stream()
                .map(user -> BeanUtil.copyProperties(user,UserDTO.class))
                .collect(Collectors.toList());

        return Result.ok(userDTOList);
    }
;