如何获取带参数的 PostgreSQL 执行计划?

问题描述 投票:0回答:1

我的应用程序有无数的查询,我想得到其中一些的执行计划。大多数如果不是 所有的查询都有多个参数,我找不到如何在 PostgreSQL 中获取任何非平凡查询的执行计划。

真实的案例要复杂得多,但这里有一个简单的、有代表性的查询案例(为简单起见,只有一个参数):

Connection conn = DriverManager.getConnection("...", "...", "...");

PreparedStatement ps1 = conn.prepareStatement(
  "prepare x as select * from documents where content = $1");
ps1.execute();

PreparedStatement ps2 = conn.prepareStatement(
  "explain (format json) execute x (?)");
ps2.setString(1, "Very long content here..."); // Binds the parameter
ResultSet rs = ps2.executeQuery(); // Error here!

while (rs.next()) {
  System.out.println(rs.getString(1));
}

当我运行它时,出现错误:

错误:没有参数 $1 位置:34

如果我硬编码参数(例如用

$1
替换
'a'
)一切正常,我得到了一个计划。但是,如果我尝试使用 JDBC 参数,它就不起作用。对参数进行硬编码对于我的用例来说是不现实的,因为它可能是一个巨大的参数,或者可能无法正确呈现为字符串(例如浮点值)。

我也尝试使用

null
而不是
?
并且它没有崩溃,但是 它返回一个错误的执行计划;似乎它以某种方式使逻辑短路并返回完全脱离现实的东西。

我做错了什么?

java sql database postgresql sql-execution-plan
1个回答
1
投票

您不需要用

绑定变量值
替换$1,但是您显然不能在
explain execute
语句中使用绑定变量——这里必须说明。

所以这个序列工作正常(伪代码)

prep = con.prepareStatement("prepare x(integer) as select id, pad from jdbn.document where id = $1")
prep.execute();

stmt = con.prepareStatement("explain execute x (42)")
rs = stmt.executeQuery()

如先前标记为重复的 question 中所述,对于前几次执行,您将获得一个 custom plan(即您在条件中看到变量,在这里

id = 42
),最后是一个 generic plan(即带有谓词
id = $1
)

你的 trick

null
失败,因为 PostgreSql 知道
id = null
什么都不返回(我使用模糊的公式来避免既不是 true 也不是 false)并制定一个 dummy 计划,例如
One-Time Filter: false

您可以强制生成一个具有plan_cache_mode

参数
通用计划

con.createStatement().execute("set plan_cache_mode = force_generic_plan")

在这种情况下,第一个解释的计划是通用的。似乎通过此设置,

null
参数不会导致虚拟计划(但我不确定是否没有其他警告)。

所以我想说你应该总是定义变量的数据类型

prepare x(integer) ...
但是我不确定并且没有经验如果你可以通过
force_generic_plan
和传递
null
的组合获得有意义的执行计划(类似于 Oracle 功能),或者如果您真的必须传递表示样本值或参数。

Final Note 这里所有的讨论都集中在精心设计的准备语句,即任何可能传递的值都将导致一个相同的执行计划。对于 other statements 它没有意义,因为没有 no single execution plan.

© www.soinside.com 2019 - 2024. All rights reserved.