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

标签 java spring kotlin spring-data-jpa

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

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

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

所以现在,我想做的基本上是从 Users 表中随机获取 20 个用户,其中性别为 MALE 并且 id 不在 [我看到的 id 列表] 中。

查询的随机性最初让我创建了一个原生查询:

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

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

所以我的第二个想法是处理代码中的随机性。所以我决定调用 db 来计算用户数量(以获取最高的 id),然后生成一些 id 值,范围从 0 到最高,过滤掉我看到的那些,然后从中获取用户按 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)

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

您能否建议如何更好地解决这个问题?

最佳答案

我并不完全熟悉 Kotlin 语言,但我会用 Java 写出逻辑,希望它能很好地为您翻译。

您的目标是获取 20 个单一性别,但在获取它之前您无法弄清楚它是什么性别。由于我们已经从数据库中提取了 20 行,我们可以提取更多行作为备用。

我们可以使用累积分布来优化我们实际需要的行数,使用这个工具:https://stattrek.com/online-calculator/binomial.aspx

假设性别分割为 50/50,则概率为 0.5。如果您的性别分布与您的需求不同,您可以调整此项。如果性别分割不是 50/50,您可以为每个性别创建单独的桶提取以获得适当的成功水平。我们希望至少有 20 场成功的比赛。

With a sample size of 60, we get a 99.6% probability that we will have 20 or more matches for the gender.

因此我们可以获取 60 个而不是 20 个,筛选出所选性别的前 20 个。如果我们没有达到 20(0.4% 的机会),则重新绘制另一组 20 来填充我们的组。所以 99% 的时间,60 行提取,在一个坏的情况下,可能是 80 行提取。这消除了在应该适用于超大型数据库的数据库端使用 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
}

关于java - 根据参数在数据库中查找固定数量的 RANDOM 用户的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56858925/

相关文章:

java - 对 Java LinkedList 的较低级别访问?

用于 jasypt 和配置文件的 Spring 4 javaconfig

spring - 如何将表单数据发送到 Controller

java - Criteria API 按嵌套聚合属性排序

android - Kotlin 中 Unresolved 异步引用

android - 如何使用 Kotlin 协程获取 API 错误主体

kotlin - kotlintest项目有一个样本结构

java - "AlreadyBuiltException: This object has already been built"构建时 "springSecurityFilterChain"

java - IntelliJ 从根项目执行 gradle 测试

java - 考虑边界条件的数的反转