将表修剪到JPQL中的500条记录

时间:2019-02-12 10:46:02

标签: java jpa jpql

我正在使用某种类型的缓存,因此有时需要根据last_access_date将表中的表修剪为500条记录(仅保留500条最近访问的行)。

使用“普通” 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真的没有办法做到这一点吗?

2 个答案:

答案 0 :(得分:3)

您可以这样做:

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();

答案 1 :(得分:2)

出于性能原因,必须不要仅加载500个ID值然后再次将其发送到服务器来执行其他客户端往返。相反,我建议使用以下两种方法之一:

使用供应商特定的SQL

您当前仅支持2个RDBMS。编写两个单独的SQL语句应该是可管理的。在这种情况下,由于仅使用Oracle和SQL Server,因此您可以使用标准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
)

如果您不经常这样做,并且可以忍受暂时的不一致,那么您甚至可以实现更快的解决方案:

Oracle

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 Server

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,您可能需要考虑使用jOOQ之类的SQL构建器。其他选择也可能存在。

免责声明:我为供应商工作。