【问题标题】:Can prepared SQL statement be rewritten by the database?准备好的 SQL 语句可以被数据库重写吗?
【发布时间】: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


【解决方案1】:

认为这在某种程度上与原始查询有关

UPDATE table_name SET column2 = column1

没有WHERE 声明。

只要我将查询更改为

UPDATE table_name SET column2 = column1 WHERE 1=1

,它就像我期望的那样工作。

抱歉,这不是科学/参考的解释,但希望这仍然可能对某人有所帮助。我之前已经将这个技巧用于不同的目的(一些遗留数据库需要WHERE 子句),它似乎也可以完成这种情况。

【讨论】:

  • 通常更新语句需要在 WHERE 子句中引用主键...我很惊讶在没有 WHERE 的情况下执行查询...我会在 WHERE 子句中包含 PK
  • @JohnRuddell:为什么where 子句是“必需的”?如果要更新表中的所有行,则不需要 where 子句。但是这种证明我怀疑Java代码中有一些层进行了这种重写。我很确定这是不是 Postgres
【解决方案2】:

有些数据库实际上会重写代码(虽然它不是数据库,而是 java ——一旦你有一个有效的连接,java 就会知道如何转换你的代码)但是你的查询是误导 Postgres 的地方。 Postgres 完全按照您的要求做了,因为该查询实际上应该影响数据库中的所有行。但如果您的计划有所不同,那么您应该让我们知道您的目标是什么

【讨论】:

  • 你有Java重写这个的参考吗?另外,我应该如何与 PostgreSQL/JDBC 驱动程序通信,以便查询不会被重写?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-05
  • 1970-01-01
相关资源
最近更新 更多