我正在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)
但是在这种情况下,我无法知道随机选择的用户的性别,因此在进行数据库调用之前,我无法按性别过滤结果。
请问如何更好地解决这个问题?
答案 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提供的其他工具)