当用户操作可以删除中间的行时,如何处理数据库分页?

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,但我对此有点困惑。剩余的项目将始终依赖于pageNoperPage 并且独立于totalResult(因为总结果在两者之间减少)。我也不能在这里使用最后 n 行,因为行数会再次减少,从而改变我想要的结果。

我也不想在方法中保留太多参数。例如-假设我在参数中传递toSatrtWith id 并从那里获取perPage 行数,我可能会得到想要的东西。但我想要一个更优雅的解决方案。

【问题讨论】:

大问题让一些用户感到困惑:),我没有得到一件事希望你能解决。当用户删除任意数量的项目时,您必须通过调用list()再次计算totalResultstotalPages,那么为什么您需要担心删除和插入行,每次都会重新计算,对吧?简述你的问题。 说有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)中的任何内容。

【讨论】:

以上是关于当用户操作可以删除中间的行时,如何处理数据库分页?的主要内容,如果未能解决你的问题,请参考以下文章

如何处理删除基于光标的分页中的先前元素?

分页查询时,当元素可以删除时,加载更多的处理办法

“不知道如何处理' nvcc 致命错误

如何处理Rails加入表关系删除?

使用模态确认删除时表的id值为null时如何处理错误?

javascript如何处理很多数据,类似分页切换