使用 Hibernate 在 table 中仅保留一定数量的记录的最佳方法
Best way to keep only a certain number of records in a table with Hibernate
我正在开发一个具有内置数据库维护功能的 Hibernate / Spring 应用程序。每 15 分钟它查看某些 tables 并根据某些参数清除旧记录。以我的 LogEntry 实体为例,我基于 2 个参数进行清除:记录有多旧以及 table 中有多少记录。在第一种情况下,我正在做这样的事情:
@Override
public int deleteExpiredEntries(int systemLogKeepTimeInDays, int systemLogMaxEntries)
{
Session session = getSession();
Query query = session.createQuery("DELETE FROM LogEntry l WHERE l.time < :p");
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.DAY_OF_YEAR, -systemLogKeepTimeInDays);
return query.setParameter("p", cal.getTime()).executeUpdate();
}
我正在尝试想出在使用类似过程运行此维护时始终保留 5000 条记录的最佳方法。我考虑过使用 Id 列并清除 Id 大于 5000 的任何内容,但这实际上会清除新记录而不是旧记录!
你会如何解决这个问题?
谢谢!
您可以尝试使用 SQL 查询本身来解决问题。
首先,您需要取得前 5000 条新记录。
SELECT id FROM LogEntry ORDER BY time DESC LIMIT 100000 OFFSET 5000;
我建议您使用 LIMIT 并根据您的需要进行设置,这样查询的执行时间就不会太长。这样,您可以尽可能多地执行查询,并且由于 ORDER BY 和 OFFSET 5000,您将始终获取最新的数据,您只保留了最新的 5000 条记录。
下一步将删除:
DELETE FROM LogEntry WHERE id IN
(SELECT id FROM ( SELECT id FROM LogEntry ORDER BY time DESC LIMIT 100000 OFFSET 5000) table_alias);
也许您想知道为什么我在子查询时使用 SELECT。那是因为我需要对我选择的结果的引用,如您所见,它被命名为 table_alias。
如果您尝试使用 IN 关键字对子查询执行 DELETE,MySql 本身将不会执行查询。你会得到错误:
This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery
官方文档(says):
In general, you cannot modify a table and select from the same table in a subquery.
Exception: The preceding prohibition does not apply if for the
modified table you are using a derived table (subquery in the FROM
clause) and that derived table is materialized rather than merged into
the outer query.
MSSQL解
DELETE FROM LogEntry WHERE id BETWEEN
(
SELECT MIN(id) FROM LogEntry ORDER BY time DESC
OFFSET 5000 ROWS
FETCH NEXT 100000 ROWS ONLY
)
AND
(SELECT MAX(id) FROM LogEntry ORDER BY time DESC
OFFSET 5000 ROWS
FETCH NEXT 100000 ROWS ONLY)
由于您在评论中要求 MSSQL 解决方案,我已经尝试想出一些办法。我还没有测试过这个查询,但我有参考可以引导我找到它:this similar topic and because you need to ignore first 5000 rows, visit thispage
我希望这至少能对您有所帮助,甚至比我更了解您可以做什么!
我正在开发一个具有内置数据库维护功能的 Hibernate / Spring 应用程序。每 15 分钟它查看某些 tables 并根据某些参数清除旧记录。以我的 LogEntry 实体为例,我基于 2 个参数进行清除:记录有多旧以及 table 中有多少记录。在第一种情况下,我正在做这样的事情:
@Override
public int deleteExpiredEntries(int systemLogKeepTimeInDays, int systemLogMaxEntries)
{
Session session = getSession();
Query query = session.createQuery("DELETE FROM LogEntry l WHERE l.time < :p");
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.DAY_OF_YEAR, -systemLogKeepTimeInDays);
return query.setParameter("p", cal.getTime()).executeUpdate();
}
我正在尝试想出在使用类似过程运行此维护时始终保留 5000 条记录的最佳方法。我考虑过使用 Id 列并清除 Id 大于 5000 的任何内容,但这实际上会清除新记录而不是旧记录!
你会如何解决这个问题?
谢谢!
您可以尝试使用 SQL 查询本身来解决问题。
首先,您需要取得前 5000 条新记录。
SELECT id FROM LogEntry ORDER BY time DESC LIMIT 100000 OFFSET 5000;
我建议您使用 LIMIT 并根据您的需要进行设置,这样查询的执行时间就不会太长。这样,您可以尽可能多地执行查询,并且由于 ORDER BY 和 OFFSET 5000,您将始终获取最新的数据,您只保留了最新的 5000 条记录。
下一步将删除:
DELETE FROM LogEntry WHERE id IN
(SELECT id FROM ( SELECT id FROM LogEntry ORDER BY time DESC LIMIT 100000 OFFSET 5000) table_alias);
也许您想知道为什么我在子查询时使用 SELECT。那是因为我需要对我选择的结果的引用,如您所见,它被命名为 table_alias。 如果您尝试使用 IN 关键字对子查询执行 DELETE,MySql 本身将不会执行查询。你会得到错误:
This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery
官方文档(says):
In general, you cannot modify a table and select from the same table in a subquery.
Exception: The preceding prohibition does not apply if for the modified table you are using a derived table (subquery in the FROM clause) and that derived table is materialized rather than merged into the outer query.
MSSQL解
DELETE FROM LogEntry WHERE id BETWEEN
(
SELECT MIN(id) FROM LogEntry ORDER BY time DESC
OFFSET 5000 ROWS
FETCH NEXT 100000 ROWS ONLY
)
AND
(SELECT MAX(id) FROM LogEntry ORDER BY time DESC
OFFSET 5000 ROWS
FETCH NEXT 100000 ROWS ONLY)
由于您在评论中要求 MSSQL 解决方案,我已经尝试想出一些办法。我还没有测试过这个查询,但我有参考可以引导我找到它:this similar topic and because you need to ignore first 5000 rows, visit thispage
我希望这至少能对您有所帮助,甚至比我更了解您可以做什么!