【发布时间】:2015-02-04 15:20:48
【问题描述】:
我有这个看起来很无辜的 JDBC 代码:
String sql = "UPDATE table_name SET column2 = column1";
try (PreparedStatement statement = dbConnection.prepareStatement(sql)) {
statement.executeUpdate();
}
dbConnection.commit();
在 PostgreSQL 上运行时,我注意到 实际运行 查询(在 PostgreSQL 中可见)如下:
UPDATE table_name SET column2 = i.column1 FROM table_name i
问题在于重写后的查询要昂贵得多:
# explain update table_name set column2 = i.column1 from table_name i;
QUERY PLAN
-------------------------------------------------------------------------------
Update on table_name (cost=0.00..3586127424.55 rows=206294914809 width=166)
-> Nested Loop (cost=0.00..3586127424.55 rows=206294914809 width=166)
-> Seq Scan on table_name (cost=0.00..15453.97 rows=454197 width=156)
-> Materialize (cost=0.00..19942.96 rows=454197 width=10)
-> Seq Scan on table_name i (cost=0.00..15453.97 rows=454197 width=10)
(5 rows)
而不是
# explain update table_name set column2 = column1;
QUERY PLAN
------------------------------------------------------------------------
Update on table_name (cost=0.00..15453.97 rows=454197 width=156)
-> Seq Scan on table_name (cost=0.00..15453.97 rows=454197 width=156)
(2 rows)
重写的查询需要几乎无限的时间来运行,而未重写的查询在几分钟甚至几秒钟内完成。
问题:
- 数据库(我认为)重写查询是否常见?
- 如果是,那么 PostgreSQL 怎么会愚蠢到自取其辱?这是一个已知的错误吗?
- 如何避免查询重写 - 无论是在数据库级别,还是在 JDBC 级别?
【问题讨论】:
-
不,Postgres 不会那样重写表格。
UPDATE table_name ... FROM table_name实际上是在创建一个笛卡尔自连接,这就是该语句如此缓慢的原因。这种说法是完全错误的。对我来说,它看起来像是由考虑 MySQL(或 SQL Server)的人(重新)编写的。您确定运行该查询不涉及其他层吗?像 JPA/Hibernate 这样的混淆层? -
这对我来说也看起来像 Hibernateish,我们使用它。但是,我进行了三次检查,以确保在发生这种情况时 Hibernate 甚至没有被初始化。
-
可能相关:Connection.nativeSQL()。不是真正的解决方案,但您可以通过自己限定
column1来避免重写。
标签: java sql postgresql jdbc