1

我必须使用 Hibernate 并从 Oracle 检索数据,但问题是,传递给查询的参数数量并不总是相同的。

为简单起见,让我们考虑以下查询:

从 TAB_1 中选择 COL_1、COL_2、...、COL_N,其中 COL_1 在 (?, ?, ... ?)

传递给 in 子句的参数数量在 1 到 500 之间。如果数量在 1-50 左右,它的工作速度非常快,但对于 200,它需要几秒钟来执行查询(解析、创建解释计划、执行查询) . 索引被创建和使用 - 它被检查过。

查询是动态创建的,所以我使用 Hibernate Criteria API。对于第一个查询(具有 > 100 个参数),它需要 3-5 秒,但对于下一个查询,它的工作速度更快(即使参数数量不同)。我想改进第一个查询的响应时间。假设 Hibernate 是必须的,在这种情况下我该怎么办?

我想删除这个动态查询,在 xml 文件中创建一些静态查询作为命名查询(在这种情况下,这些查询将在开始时预编译)例如

1) 如果参数个数少于 50,则进行一次查询。

在这种情况下,如果我们有 30 个参数,那么查询将如下所示:

从 TAB_1 中选择 COL_1, COL_2, ..., COL_N 其中 COL_1 在 (PAR_1, PAR_2, ..., PAR_30, -1, -1 , ..., -1 ?)

2)第二个,如果数字在 50 到 100 之间,等等。

问题是使用命名查询和 HQL 并不是那么简单(在 JDBC 中它会很简单)。在 HQL 中,我们只传递了一个列表,并且我们没有在该列表中指定多个参数,即实际上只有一个查询

'from Person where id in (:person_list)'

myQuery.setParameterList("person_list", myList)

有没有办法解决这个问题?

顺便说一句,我认为解释计划是针对每个新查询执行的,例如:

(a) select COL_1, COL_2, ..., COL_N from TAB_1 where COL_1 in (?, ?, ..., ?) <100> - 必须创建解释计划

(b) select COL_1, COL_2, ..., COL_N from TAB_1 where COL_1 in (?, ?, ..., ?) <100> - 解释计划不会被创建,因为它已经存在于缓存中

(c) select COL_1, COL_2, ..., COL_N from TAB_1 where COL_1 in (?, ?, ..., ?) <120> - 应该创建解释计划(对于具有 120 个参数的查询没有解释计划) 但与 (a) 相比,它花费的时间更少,几乎与 (b) 相同,因此如果之前执行类似的查询,Oracle 可能可以更快地创建此计划

这是什么原因?

4

2 回答 2

1

这里有几件事。首先,你不能绑定一个 IN 列表,至少我很确定你不能。我怀疑 Hibernate 正在使用某种技巧,您将数组内容放入 Oracle 可以使用的静态 inlist 中。

其次,如果这个查询使用很多不同的参数执行,你必须绑定变量,否则整个数据库的性能会受到影响。

也就是说,有一种方法可以使用 Tom Kyte 在他的博客上描述的“技巧”来绑定 IN 列表 -

http://tkyte.blogspot.com/2006/01/how-can-i.html

那里的代码如下所示:

ops$tkyte@ORA10GR2> with bound_inlist
2  as
3  (
4  select
5    substr(txt,
6           instr (txt, ',', 1, level  ) + 1,
7           instr (txt, ',', 1, level+1) - instr (txt, ',', 1, level) -1 )
8           as token
9    from (select ','||:txt||',' txt from dual)
10  connect by level <= length(:txt)-length(replace(:txt,',',''))+1
11  )
12  select *
13    from all_users
14   where user_id in (select * from bound_inlist);

USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
SYSTEM                                  5 30-JUN-05
OPS$TKYTE                             104 20-JAN-06

那个部分:

12  select *
13    from all_users
14   where user_id in (select * from bound_inlist);

基本上是您的查询所在。上面的位是将逗号分隔的字符串拆分为值列表的技巧。您不需要将列表绑定到 :txt 占位符,而是需要将列表转换为字符串并绑定它。

您确定查询时间的差异不是由于机器上的缓存或负载变化造成的吗?解析查询需要一点时间,但几秒钟是很长的时间。

于 2009-11-12T13:02:03.440 回答
0

我处理过IN(...)该列表中最多有 1000 个 id 的查询;我可以向您保证,解析/准备/缓存语句不需要几秒钟。

Hibernate 确实使用您传递的列表中元素的实际数量自动扩展您指定的参数列表,因此如果您真的想将其“固定”在某个级别,您需要做的就是附加足够的 -1 到结束。但是,这肯定不是问题,尤其是因为我们正在讨论加快第一次查询运行的速度——无论如何,还没有准备/缓存任何语句。

您是否查看了查询的执行计划?通过解释计划和自动跟踪都启用了吗?当您的列表中有 30 个元素和 120 个元素时,它们会有所不同吗?您的实际查询是否真的看起来像您发布的“从表中选择 ... 我敢打赌,Oracle 决定(可能是错误地)不使用索引会更快,这就是为什么您看到时间增加的原因在 30 到 120 个元素之间。

于 2009-11-12T17:55:51.400 回答