[英]How to write an efficient JPQL query that returns list of linked records in a table?
[英]Prune table to 500 records in 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()
(JPQLs版本的LIMIT
)对于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值然后再次将其发送到服务器来执行其他客户端往返。 相反,我建议使用以下两种方法之一:
您目前仅支持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
)
如果您不经常这样做,并且可以忍受暂时的不一致,那么您甚至可以实现更快的解决方案:
甲骨文
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,您可能需要研究使用诸如jOOQ之类的SQL构建器。 可能存在其他替代方案。
免责声明:我为供应商工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.