通过 RAW plpgsql 中的 UPDATE 语句获取受影响的行

Posted

技术标签:

【中文标题】通过 RAW plpgsql 中的 UPDATE 语句获取受影响的行【英文标题】:Getting Affected Rows by UPDATE statement in RAW plpgsql 【发布时间】:2012-11-30 15:14:33 【问题描述】:

这已被多次询问here 和here,但没有一个答案适合我的情况,因为我不想在 PL/PgSQL 函数中执行我的更新语句并使用GET DIAGNOSTICS integer_var = ROW_COUNT

我必须在原始 SQL 中执行此操作。

例如,在 MS SQL SERVER 中,我们有 @@ROWCOUNT 可以像下面这样使用:

UPDATE <target_table> 
SET Proprerty0 = Value0
WHERE <predicate>;
SELECT <computed_value_columns> 
 FROM <target> 
 WHERE @@ROWCOUNT > 0;

在数据库的一次往返中,我知道更新是否成功并返回计算值。

可以用什么来代替 '@@ROWCOUNT' ? 有人可以确认目前这实际上是不可能的吗?

提前致谢。

EDIT 1:我确认我需要使用原始 SQL(我在原始描述中写了“raw plpgsql”)。

为了使我的问题更清楚,请考虑更新语句仅影响一行并考虑乐观并发:

    客户一开始做了SELECT声明。

    他构建了 UPDATE 并知道哪些数据库计算列将包含在 SELECT 子句中。除其他外,谓词包括每次更新行时计算的时间戳

    因此,如果我们返回 1 行,那么一切正常。如果没有返回任何行,那么我们知道之前有更新,客户端可能需要在再次尝试更新子句之前刷新数据。这就是为什么我们需要在返回计算列之前知道有多少行受更新语句影响。如果更新失败,则不应返回任何行。

【问题讨论】:

请始终在问题中提及您的 PostgreSQL 版本。 所以你不想想在 PL/pgSQL 中但在 SQL 中做。否则你可以使用GET DIAGNOSTICS @CraigRinger : 查看相应的标签。 重新版本:我的错误。我忘记了人们使用特定于版本的标签只是为了说“这是我正在使用的版本”。 我已经根据您的编辑详细说明了我的答案。看起来 UPDATE ... RETURNING 可以满足您的需求。 【参考方案1】:

目前无法以您描述的形式实现您想要的,但我认为您可以使用UPDATE ... RETURNING 做您想做的事情。见UPDATE ... RETURNING in the manual。

UPDATE <target_table> 
SET Proprerty0 = Value0
WHERE <predicate>
RETURNING Property0;

很难确定,因为你提供的例子太抽象了,有点毫无意义。

您还可以使用 wCTE,它允许更复杂的情况:

WITH updated_rows AS (
    UPDATE <target_table> 
    SET Proprerty0 = Value0
    WHERE <predicate>
    RETURNING row_id, Property0
)
SELECT row_id, some_computed_value_from_property
FROM updated_rows;

参见common table expressions (WITH queries) 和depesz's article on wCTEs。


更新基于问题中的一些附加细节,这里有一个使用UPDATE ... RETURNING的演示:

CREATE TABLE upret_demo(
  id serial primary key,
  somecol text not null,
  last_updated timestamptz
);

INSERT INTO upret_demo (somecol, last_updated) VALUES ('blah',current_timestamp);

UPDATE upret_demo
SET
  somecol = 'newvalue',
  last_updated = current_timestamp
WHERE last_updated = '2012-12-03 19:36:15.045159+08'    -- Change to your timestamp
RETURNING 
  somecol || '_computed' AS a,
  'totally_new_computed_column' AS b;

第一次运行时的输出:

         a         |              b              
-------------------+-----------------------------
 newvalue_computed | totally_new_computed_column
(1 row)

再次运行时,它没有任何效果,也不返回任何行。

如果您在结果集中有更复杂的计算要做,您可以使用 wCTE,这样您就可以 JOIN 更新的结果并做其他复杂的事情。

WITH upd_row AS (
  UPDATE upret_demo SET 
    somecol = 'newvalue',
    last_updated = current_timestamp
  WHERE last_updated = '2012-12-03 19:36:15.045159+08'
  RETURNING id, somecol, last_updated
)
SELECT
  'row_'||id||'_'||somecol||', updated '||last_updated AS calc1,
 repeat('x',4) AS calc2
FROM upd_row;

换句话说:使用UPDATE ... RETURNING,可以直接生成计算行,也可以在可写的CTE 中处理更复杂的情况。

【讨论】:

可写 CTE 从 9.2 版开始工作。可写 CTE 很棒!我不明白为什么他们花了这么长时间才实施,大多数 DBMS 品牌仍然不支持它,我不能没有它了。【参考方案2】:

这个问题的答案通常取决于所使用的驱动程序的类型。

如果应用程序使用 libpq,PQcmdTuples() 函数会执行所需的操作。 libpq 之上的其他库需要在此函数之上有一些包装器。

对于 JDBC,Statement.executeUpdate() 方法似乎很适合。

ODBC 为类似的目的提供了 SQLRowCount() 函数。

【讨论】:

以上是关于通过 RAW plpgsql 中的 UPDATE 语句获取受影响的行的主要内容,如果未能解决你的问题,请参考以下文章

无法执行 plpgsql/postgres 中的函数

检查 plpgsql 中的当前日志记录级别

如何从plpgsql中的循环返回结果?

无法从 plpgsql 函数中的动态命名临时表运行“选择进入”

从 plpgsql 中的 FOR 循环切换到基于集合的 SQL 命令

sql plpgsql存储过程中的动态查询示例