在 JPQL 中修剪 table 到 500 条记录
Prune table to 500 records in JPQL
我在某种缓存上工作,有时,我们需要根据 last_access_date
(只保留500 个最近访问的行)。
使用 "plain" SQL,可以通过以下方式完成:
DELETE FROM records WHERE id not in
(SELECT id FROM records ORDER BY last_access_date DESC LIMIT 500)
现在 JPQL 中没有 LIMIT
或类似 ROWNUM
的东西,我找到的唯一解决方案是原生 SQL,这是次优的,因为我们 运行 在多个 DBMS 上(至少是 Oracle 和 MSSQL)。
此外,setMaxResults()
(LIMIT
的 JPQL 版本)似乎对 DELETE
语句无效。
真的没有办法用 JPQL 解决这个问题吗?
你可以这样做:
String sql = "SELECT x.id FROM records x ORDER BY x.last_access_date DESC";
TypedQuery<Long> query = em.createQuery(sql, Long.class);
List<Long> ids = query.setMaxResults(500).getResultList();
String delete = "DELETE FROM records x where x.id not in :ids";
em.createQuery(delete).setParameter("ids", ids).executeUpdate();
我不记得删除查询的确切语法,因此您可能需要将 :ids
放在括号之间,例如:
String delete = "DELETE FROM records x where x.id not in (:ids)";
编辑: dkb 提出了一个更快的评论解决方案(取决于唯一日期以确保剩余行数的完美准确性):
String sql = "SELECT x.last_access_date FROM records x ORDER BY x.last_access_date DESC";
//If you're not using calendar, change to your specific date class
TypedQuery<Calendar> query = em.createQuery(sql, Calendar.class);
Calendar lastDate = query.setFirstResult(499).setMaxResults(1).getSingleResult();
String delete = "DELETE FROM records x where x.last_access_date < :lastDate";
em.createQuery(delete).setParameter("lastDate", lastDate, TemporalType.DATE).executeUpdate();
出于性能原因,必须不要为了加载 500 个 ID 值而进行额外的客户端往返,然后再次将它们发送到服务器。相反,我会建议以下两种方法之一:
使用特定供应商SQL
您目前仅支持 2 个 RDBMS。编写两个单独的 SQL 语句应该是易于管理的。在这种情况下,由于您仅使用 Oracle 和 SQL 服务器,因此您可以使用标准 SQL 实现这一点,事实上:
DELETE FROM records
WHERE id NOT IN (
SELECT id
FROM records
ORDER BY last_access_date DESC
OFFSET 0 ROWS -- SQL Server needs this
FETCH FIRST 500 ROWS ONLY
)
如果您不经常这样做并且可以忍受暂时的不一致,您甚至可以实施更快的解决方案:
甲骨文
CREATE TABLE temp AS
SELECT *
FROM records
ORDER BY last_access_date DESC
FETCH FIRST 500 ROWS ONLY;
TRUNCATE TABLE records;
INSERT INTO records
SELECT * FROM temp;
DROP TABLE temp;
SQL 服务器
SELECT TOP 500 *
INTO temp
FROM records
ORDER BY last_access_date DESC;
TRUNCATE TABLE records;
INSERT INTO records
SELECT * FROM temp;
DROP TABLE temp;
使用 SQL 生成器
对于更复杂的供应商不可知论者 SQL,您可能需要考虑使用 SQL 构建器,例如 jOOQ。可能存在其他替代方案。
免责声明:我为供应商工作。
我在某种缓存上工作,有时,我们需要根据 last_access_date
(只保留500 个最近访问的行)。
使用 "plain" SQL,可以通过以下方式完成:
DELETE FROM records WHERE id not in
(SELECT id FROM records ORDER BY last_access_date DESC LIMIT 500)
现在 JPQL 中没有 LIMIT
或类似 ROWNUM
的东西,我找到的唯一解决方案是原生 SQL,这是次优的,因为我们 运行 在多个 DBMS 上(至少是 Oracle 和 MSSQL)。
此外,setMaxResults()
(LIMIT
的 JPQL 版本)似乎对 DELETE
语句无效。
真的没有办法用 JPQL 解决这个问题吗?
你可以这样做:
String sql = "SELECT x.id FROM records x ORDER BY x.last_access_date DESC";
TypedQuery<Long> query = em.createQuery(sql, Long.class);
List<Long> ids = query.setMaxResults(500).getResultList();
String delete = "DELETE FROM records x where x.id not in :ids";
em.createQuery(delete).setParameter("ids", ids).executeUpdate();
我不记得删除查询的确切语法,因此您可能需要将 :ids
放在括号之间,例如:
String delete = "DELETE FROM records x where x.id not in (:ids)";
编辑: dkb 提出了一个更快的评论解决方案(取决于唯一日期以确保剩余行数的完美准确性):
String sql = "SELECT x.last_access_date FROM records x ORDER BY x.last_access_date DESC";
//If you're not using calendar, change to your specific date class
TypedQuery<Calendar> query = em.createQuery(sql, Calendar.class);
Calendar lastDate = query.setFirstResult(499).setMaxResults(1).getSingleResult();
String delete = "DELETE FROM records x where x.last_access_date < :lastDate";
em.createQuery(delete).setParameter("lastDate", lastDate, TemporalType.DATE).executeUpdate();
出于性能原因,必须不要为了加载 500 个 ID 值而进行额外的客户端往返,然后再次将它们发送到服务器。相反,我会建议以下两种方法之一:
使用特定供应商SQL
您目前仅支持 2 个 RDBMS。编写两个单独的 SQL 语句应该是易于管理的。在这种情况下,由于您仅使用 Oracle 和 SQL 服务器,因此您可以使用标准 SQL 实现这一点,事实上:
DELETE FROM records
WHERE id NOT IN (
SELECT id
FROM records
ORDER BY last_access_date DESC
OFFSET 0 ROWS -- SQL Server needs this
FETCH FIRST 500 ROWS ONLY
)
如果您不经常这样做并且可以忍受暂时的不一致,您甚至可以实施更快的解决方案:
甲骨文
CREATE TABLE temp AS
SELECT *
FROM records
ORDER BY last_access_date DESC
FETCH FIRST 500 ROWS ONLY;
TRUNCATE TABLE records;
INSERT INTO records
SELECT * FROM temp;
DROP TABLE temp;
SQL 服务器
SELECT TOP 500 *
INTO temp
FROM records
ORDER BY last_access_date DESC;
TRUNCATE TABLE records;
INSERT INTO records
SELECT * FROM temp;
DROP TABLE temp;
使用 SQL 生成器
对于更复杂的供应商不可知论者 SQL,您可能需要考虑使用 SQL 构建器,例如 jOOQ。可能存在其他替代方案。
免责声明:我为供应商工作。