当用户操作可以删除中间的行时,如何处理数据库分页?
Posted
技术标签:
【中文标题】当用户操作可以删除中间的行时,如何处理数据库分页?【英文标题】:How to deal with database pagination when a row in between can be deleted by user action? 【发布时间】:2015-04-28 11:04:29 【问题描述】:我陷入了困境。我希望我能在这里得到解决方案。我正在使用分页在用户个人资料页面上显示一些数据。
使用基本分页来做到这一点:
public Page list(Query query, int pageNo, int perPage)
int totalResults = query.list().size();
int firstResult = (pageNo - 1) * perPage;
query.setFirstResult(firstResult);
query.setMaxResults(perPage);
List resultList = query.list();
return new Page(resultList, perPage, pageNo, totalResults);
页面类构造函数:
public Page(List resultList, int pageSize, int page, int totalResults)
this.resultList = resultList;
this.pageSize = pageSize;
this.page = page;
this.totalResults = totalResults;
this.totalPages = (totalResults - 1) / pageSize + 1;
现在,如果结果在没有任何删除的情况下正常显示,这将正常工作。但是用户可以从他的操作列表中删除项目。
让我们看看情况。假设有 totalResults
- 17、perPage
- 6 和 totalPages
- 3。
现在在第一页中,他删除了 2 个项目,总共留下 15 个结果,并且本应在第 2 页中的行 id 已转移到 9 而不是 7(因为两个项目不存在,现在总结果是15)。
我希望情况很清楚。现在不需要这种中间移位。
我正在考虑将其实现为remainingItems
,但我对此有点困惑。剩余的项目将始终依赖于pageNo
和perPage
并且独立于totalResult
(因为总结果在两者之间减少)。我也不能在这里使用最后 n 行,因为行数会再次减少,从而改变我想要的结果。
我也不想在方法中保留太多参数。例如-假设我在参数中传递toSatrtWith
id 并从那里获取perPage
行数,我可能会得到想要的东西。但我想要一个更优雅的解决方案。
【问题讨论】:
大问题让一些用户感到困惑:),我没有得到一件事希望你能解决。当用户删除任意数量的项目时,您必须通过调用list()
再次计算totalResults
,totalPages
,那么为什么您需要担心删除和插入行,每次都会重新计算,对吧?简述你的问题。
说有13个结果,page1-(6), page2-(6), page3-(1)。当在 page2 用户删除某些内容时,数据库计数变为 12(而不是 13),因此在获取第三页时,totalResult 为 12,并且现在无法在第三页上获取最后一个结果。这是因为重新计算了 totalResult。
正如@Amogh 建议的那样,最简单的解决方案是重新查询每个已删除的行。
@gknicker 不,但我得到了 OP 想说的话,考虑一个视图,他将页面显示为 1,2,3 有链接现在可以在该页面上查看结果,因为他评论了如果另一个用户删除最后一条记录,当前用户点击 3 页..我正确吗?
您可以在用户每次在页面之间切换时重新计算,因此如果其他人删除了任何内容并影响了结果 - 您可以向用户显示现在相关的任何结果 - 重新计算您之前计划显示的内容.例如,如果用户在第 2 页,转到第 3 页,但第 3 页的结果不长于 - 再次显示第 2 页,但让用户了解“发生了一些事情”。
【参考方案1】:
简答:记住“你离开的地方”而不是计算位置。
长答案:请参阅我的博客,了解为什么“通过 LIMIT 和 OFFSET 进行分页是邪恶的”:http://mysql.rjweb.org/doc.php/pagination。 (您正在使用 LIMIT 和 OFFSET 的 PHP 等效项,因此该博客适用。)
[Next] 的 url 将包含此页面上最后一项的 id
,然后在构建下一页时使用 WHERE id > $id
。 (或此页面上第一项的id
,然后是WHERE id >= $id
。当在两者之间插入一行时,这种细微的差异会产生轻微的影响。)SQL 中也有这个:ORDER BY id ASC LIMIT 10
。无论是哪个页面,它都只会获取 10 行。如果没有这个技巧,您将获取当前页面之前所有页面的所有行。
[Prev] 页面类似,但有一个标志表示要倒退 (ORDER BY id DESC LIMIT 10
)。
[First] 最好不要指定 id;然后代码会说“哦,我应该构建第一页。”
[Last] 可以包含 Last 或 id=-1
或其他标志 - 然后在 SQL 中使用 ORDER BY id DESC LIMIT 10
之类的东西。
如果没有这种技术,在当前页面之前删除或插入的行会导致列表移动一个项目(向前或向后),因此,用户会错过(糟糕?)一行或重复一行(只是烦人)。
【讨论】:
我的解决方案还处理了新插入项目的相关问题。 是的,没错。除了在 ui 上发送获取记录的最后一个 id,然后查询最后一个 id 之外的 n 个项目之外,我也想不出任何其他方法。但是您是否认为这是一个优雅的解决方案,而且对于第一页,由于 id 不可用,我们采用了粗略的方式。?您能否在答案中也提及这些要点(简而言之)?谢谢 完成。还需要什么吗? @RickJames 如何处理新插入的行。当在 N 页中进行分页时,例如是第 3 页,那么在第 1 页中插入了新数据,如何向用户显示该新数据。注意。我们已经声明用户当前请求第三页 @Muklas - 使用OFFSET
会导致细微的错误。如果您位于每 10 行的第 3 页,则您的页面“之前”有 20 行。如果在此处插入一行,则现在有 21 行。使用OFFSET
,[Prev] 会询问第 21-30 行,跳过一行。使用“返回 10 行”,您将得到 22-31 并且不会跳过任何行。等等。【参考方案2】:
我遇到了完全相同的问题。
我的解决方案是以相反的顺序处理每一页,即我从最后一页开始,然后是倒数第二页,一直到第一页。这样,即使我删除了第 N+1 页中的一些行,这也不会改变第 N 页(或 N-1,N-2,..,1)中的任何内容。
【讨论】:
以上是关于当用户操作可以删除中间的行时,如何处理数据库分页?的主要内容,如果未能解决你的问题,请参考以下文章