Hibernate ORM中分页期间的2个查询

Hibernate ORM中分页期间的2个查询,hibernate,sqlalchemy,orm,flask-sqlalchemy,Hibernate,Sqlalchemy,Orm,Flask Sqlalchemy,我开始深入研究ORMs,突然一个问题出现在我的脑海中: 当我们使用它们的分页功能时,像Hibernate和SqlAlchemy这样的ORM会触发2查询吗 如果否,那么在设置pageNo和pageSize的情况下,它们如何显示总结果集计数 如果是,它是否有效?对表中的所有记录运行额外查询以获取实际计数,然后使用LIMIT和OFFSET flask sqlalchemy中的分页对象提供了类似于obj.total,obj.has\u next,obj.pagesqlalchemy的属性,只需两个查

我开始深入研究ORMs,突然一个问题出现在我的脑海中: 当我们使用它们的分页功能时,像Hibernate和SqlAlchemy这样的ORM会触发2查询吗

  • 如果否,那么在设置pageNo和pageSize的情况下,它们如何显示总结果集计数
  • 如果是,它是否有效?对表中的所有记录运行额外查询以获取实际计数,然后使用
    LIMIT
    OFFSET

flask sqlalchemy
中的分页对象提供了类似于
obj.total
obj.has\u next
obj.page
sqlalchemy的属性,只需两个查询,就可以正常工作了——实际上没有其他方法可以做到这一点

例如,使用一个简单的
Post
模型,您会得到两个查询(我已经在配置中将
SQLALCHEMY\u ECHO
设置为
True
,以获得这些语句的回显)

正如您所看到的——实际上首先触发的是偏移量,它并没有将您限制在语句级别的实际返回行(您将不会得到任何返回)

这样做有效吗?好吧,这是最有效的,因为它可以给问题。我相信最近有一个对Flask SQLAlchemy的承诺,所以在即将发布的版本中,您可以选择性地禁用
count(*)
second查询,但是很明显,如果您的用户界面需要这样的页面,那么您不知道您总共有多少页面,所以这是取舍

您的数据库将尽其所能提高效率,如果是普通查询,可能会缓存结果,可能会注意到它只命中索引列,并跳过完整表扫描。例如,如果我使用SQLite重新运行相同的命令,我会得到:

>>> p = Post.query.paginate(per_page=20, page=5)
2021-05-05 15:02:21,403 INFO sqlalchemy.engine.Engine SELECT post.id AS post_id, post.user_id AS post_user_id, post.title AS post_title, post.content AS post_content, post.dttm AS post_dttm 
FROM post
 LIMIT ? OFFSET ?
2021-05-05 15:02:21,403 INFO sqlalchemy.engine.Engine [cached since 61.8s ago] (20, 80)
2021-05-05 15:02:21,404 INFO sqlalchemy.engine.Engine SELECT count(*) AS count_1 
FROM (SELECT post.id AS post_id, post.user_id AS post_user_id, post.title AS post_title, post.content AS post_content, post.dttm AS post_dttm 
FROM post) AS anon_1
2021-05-05 15:02:21,404 INFO sqlalchemy.engine.Engine [cached since 61.8s ago] ()

请注意,它是如何缓存早期的结果的,因此会立即返回。因此,虽然这不是一个令人愉快的答案——它的效率与一般情况下一样高。

SQLAlchemy的工作原理与您预期的一样,有两个查询——但实际上没有其他方法可以做到这一点

例如,使用一个简单的
Post
模型,您会得到两个查询(我已经在配置中将
SQLALCHEMY\u ECHO
设置为
True
,以获得这些语句的回显)

正如您所看到的——实际上首先触发的是偏移量,它并没有将您限制在语句级别的实际返回行(您将不会得到任何返回)

这样做有效吗?好吧,这是最有效的,因为它可以给问题。我相信最近有一个对Flask SQLAlchemy的承诺,所以在即将发布的版本中,您可以选择性地禁用
count(*)
second查询,但是很明显,如果您的用户界面需要这样的页面,那么您不知道您总共有多少页面,所以这是取舍

您的数据库将尽其所能提高效率,如果是普通查询,可能会缓存结果,可能会注意到它只命中索引列,并跳过完整表扫描。例如,如果我使用SQLite重新运行相同的命令,我会得到:

>>> p = Post.query.paginate(per_page=20, page=5)
2021-05-05 15:02:21,403 INFO sqlalchemy.engine.Engine SELECT post.id AS post_id, post.user_id AS post_user_id, post.title AS post_title, post.content AS post_content, post.dttm AS post_dttm 
FROM post
 LIMIT ? OFFSET ?
2021-05-05 15:02:21,403 INFO sqlalchemy.engine.Engine [cached since 61.8s ago] (20, 80)
2021-05-05 15:02:21,404 INFO sqlalchemy.engine.Engine SELECT count(*) AS count_1 
FROM (SELECT post.id AS post_id, post.user_id AS post_user_id, post.title AS post_title, post.content AS post_content, post.dttm AS post_dttm 
FROM post) AS anon_1
2021-05-05 15:02:21,404 INFO sqlalchemy.engine.Engine [cached since 61.8s ago] ()

请注意,它是如何缓存早期的结果的,因此会立即返回。因此,虽然这不是一个令人愉快的答案,但它的效率一般来说是一样的。

有时不需要知道确切的项目有多少,但只要知道还有更多的元素就足够了。Spring数据调用此
切片
,而不是
页面
。本质上,如果页面大小为N,它将获取N+1行,以便能够告诉您有更多的元素要获取。这对键集分页最有效。不过,您可以将count查询作为select项内联到主查询中,这是(在JPA/Hibernate之上工作)默认情况下所做的。所有现代数据库都应该能够发现count查询是不相关的,因此只执行一次,而不是每行执行一次


ORM通常不做的一个优化是对计数查询进行连接修剪。Blaze Persistence将很高兴地删除所有连接和其他不必要的子句,并因此大大提高性能。另一种改进计数的方法是避免计算所有元素,而是只计算到某个界限。通常,最终用户并不关心您是找到了101个结果还是1000个结果,那么为什么要将它们全部计算在内呢?Blaze Persistence可以很容易地生成这样一个有界计数查询:

有时候,不需要确切地知道有多少项,但只要知道还有更多的元素就足够了。Spring数据调用此
切片
,而不是
页面
。本质上,如果页面大小为N,它将获取N+1行,以便能够告诉您有更多的元素要获取。这对键集分页最有效。不过,您可以将count查询作为select项内联到主查询中,这是(在JPA/Hibernate之上工作)默认情况下所做的。所有现代数据库都应该能够发现count查询是不相关的,因此只执行一次,而不是每行执行一次

ORM通常不做的一个优化是对计数查询进行连接修剪。Blaze Persistence将很高兴地删除所有连接和其他不必要的子句,并因此大大提高性能。另一种改进计数的方法是避免计算所有元素,而是只计算到某个界限。通常,最终用户并不关心您是找到了101个结果还是1000个结果,那么为什么要将它们全部计算在内呢?Blaze Persistence可以轻松生成这样一个有界计数查询: