根据参数在数据库中查找固定数量的RANDOM用户的最佳方法是什么?

时间:2019-07-02 19:27:38

标签: java spring kotlin spring-data-jpa

我正在Kotlin中开发Spring Boot REST API。基础数据库是Postgresql,我正在使用Spring Data JPA进行数据库访问。

我有一个名为“ Users”的表,其中有一些用户数据。用户属性之一是“性别”。它可以具有两个值之一:MALE或FEMALE。

我想在我的应用程序中使用一个功能,以查找一个我以前从未见过的随机数(例如20个)的指定性别的人。我的意思是-假设我有一个表,用于存储已经见过的用户ID。

所以现在,我要做的基本上是从“用户”表中获得20个随机用户,其中性别为MALE,id不为[我见过的ID列表]。

查询的随机性最初使我创建了一种本地查询:

SELECT *  FROM users WHERE gender = :gender ORDER BY random() LIMIT :number

但是,我意识到这可能效率很低,因为order by random()部分将对整个表格进行排序(如果我选择一种性别,则为表格的一半)。

所以我的第二个想法是照顾代码中的随机性。因此,我决定进行一次数据库调用以计算用户数量(以获取最高的ID),然后生成从0到最高范围的ID值,过滤掉我看到的ID值,然后从中获取用户数据库按ID:

val numberOfUsersInDatabase = userRepository.count()
    val idsOfUsersVotedForBefore = voteService.findIdsOfUsersVotedFor(requestingUser.id!!)
    val excludedIds = idsOfUsersVotedForBefore.plus(requestingUser.id)

    val idsToFetch = random.longs(2*amountOfIds, 1L, numberOfUsersInDatabase)
            .boxed()
            .filter { num -> !excludedIds.contains(num) }
            .limit(amountOfIds)
            .collect(toSet())
   val randomUsers = userRepository.findUsersByIds(idsToFetch)

但是在这种情况下,我无法知道随机选择的用户的性别,因此在进行数据库调用之前,我无法按性别过滤结果。

请问如何更好地解决这个问题?

4 个答案:

答案 0 :(得分:2)

我并不完全熟悉Kotlin语言,但是我将用Java编写逻辑,并希望它可以从那里很好地翻译为您。

您的目标是获取20个单一性别,但是直到获取性别,您才能弄清楚它是什么性别。由于我们已经从数据库中获取了20行,因此我们可以获取更多的备用数据。

使用此工具,我们可以使用累积分布来获得我们实际需要多少行的最佳计数:https://stattrek.com/online-calculator/binomial.aspx

假设性别细分为50/50,则概率为0.5。如果您的性别分布与您的需求不同,则可以进行调整。如果性别细分不是50/50,则可以为每种性别创建单独的存储区抓取,以获取适当的成功级别。我们希望至少有20场成功比赛。

  

使用60个样本,我们有 99.6%可能性,我们将有20个或更多的性别匹配项。

因此,我们可以提取60个而不是20个,过滤所选性别的前20个。如果我们没有达到20(有0.4%的机会),那么请重新绘制20组以填补我们的团队。因此,有99%的情况下,一次坏的读取可能是60行,可能是80行。这样就消除了在数据库端使用RAND的任何情况,而RAND应该适合于超大型数据库。

Set<Long> idsToFetch = random.longs(2*amountOfIds, 1L, numberOfUsersInDatabase)
            .boxed()
            .filter { num -> !excludedIds.contains(num) }
            .limit(amountOfIds * 3)
            .collect(toSet());

List<User> randomUsers = userRepository.findUsersByIds(idsToFetch);

List<User> selectedUsers = randomUsers
                            .stream()
                            .filter(e -> e.gender == selectedGender)
                            .limit(amountOfIds)
                            .collect(toList());


if(selectedUsers.length < amountOfIds) { 
    //redo or single fetch operation
}

答案 1 :(得分:1)

根据您的条件选择10万个id。内存中大约几MB的数据。只需将其随机播放即可。然后select * from tables in(id1,id2...,id20)

答案 2 :(得分:0)

不生成ID,而是生成行索引。

然后循环执行此操作

select top 1 start at :randomBase *
from users where gender = :gender

答案 3 :(得分:0)

关于帖子中查询的实例化视图如何。可以按照您选择的时间表安排刷新(使用cron作业或Postgres提供的其他工具)