在Postgres中实现SELECT的UPDATE

时间:2012-03-29 04:24:59

标签: postgresql

我意识到Postgres中没有TRIGGER ON SELECT。给出一个像这样的表

CREATE TABLE t (
    a INTEGER PRIMARY KEY, 
    b TEXT, 
    entered_by INTEGER, 
    qry_count INTEGER
);

我想为每个“entered_by”增加每个SELECT的“qry_count”,基本上跟踪每个“enterer”查询任何记录的次数。例如,

SELECT * a, b FROM t WHERE <condition>;

可能返回不同的enterers输入的“n”行。对于每个进入者,我想要qry_count ++。前面的伪代码

FOR EVERY entered_by IN SELECT
    UPDATE t
    SET qry_count = qry_count + 1
    WHERE entered_by = <entered_by>

我可以在我的应用程序中最容易地做到这一点,但我想知道在数据库中执行此操作可能是最好的。我找到了example我认为我想去的地方,但它适用于PL / SQL。使用Pg实现这一目标的最佳方法是什么?

更新:在Perl中,我会这样做

$sth_sel = $dbh->prepare( .. complicated SELECT includes "entered_by" ..);
$sth_upd = $dbh->prepare("UPDATE t SET qry_count = qry_count + 1 WHERE entered_by = ?");

$sth_sel->execute( .. bind params ..);
while (my $r = $sth_sel->fetchrow_arrayref) {
    my $entered_by = $r->[ 7 ]; # or whatever
    $sth_upd->execute($entered_by);

    .. do other things with $sth_sel, perhaps build a JSON obj to return ..
}

这看起来似乎最简单,但将此功能构建为数据模式的核心部分会很好。

UPDATE2:大多数示例(包括下面的建议)都依赖于创建PL / PgSQL函数。这种方法的问题是我的查询在函数中是硬编码的。即使它可以接受输入参数,它仍然是一个预先声明的查询。这意味着我必须为每个查询创建一个单独的函数。实际上,在我的应用程序中,我根据用户请求的内容(通过Web)动态构建查询。请求的列可以更改,提供的参数可以更改。我想我正在寻找一个类似于上面的Perl伪代码的SQL而没有预先声明的函数(下面的SQL伪代码)

BEGIN
    FOR row IN
        SELECT to my hearts content
        FROM whatever tables I want JOINed horrendously
        WHERE any arbitrary param
    LOOP
        eb := row[entered_by]
        UPDATE t SET qry_count = qry_count + 1 WHERE entered_by = eb
        RETURN NEXT row;
    END LOOP;
    RETURN;
END

希望这能使我的目标更清晰。

3 个答案:

答案 0 :(得分:2)

您可以使用RETURNING

来使用UPDATE,而不是SELECT查询
UPDATE t 
SET 
  qry_count = qry_count + 1
WHERE 
  entered_by = <entered_by>
RETURNING a, b;

如果要更新计数器并将结果连接到另一个表,可以使用公用表表达式。使用UPDATE的CTE从版本9.1开始提供。

WITH cte AS (
    UPDATE t 
    SET 
      qry_count = qry_count + 1
    WHERE 
      entered_by = <entered_by>
    RETURNING a, b
)
SELECT 
    * 
FROM cte
    JOIN other_table ON <..> = <..>;

答案 1 :(得分:2)

请注意qry_count列的默认值:

CREATE TABLE t (
    a INTEGER PRIMARY KEY, 
    b TEXT, 
    entered_by INTEGER, 
    qry_count INTEGER default 0
);

create function select_and_update(parameter text)
returns setof t as $$
    update t
    set qry_count = qry_count + 1
    from (
        select a
        from t
        where b = $1
        ) s
    where t.a = s.a
    ;
    select *
    from t
    where b = $1
    ;
$$ language sql;

现在使用上述函数查询表:

select * from select_and_update('a');

根据评论更新:

你可以动态地构建它,而不是函数只是在事务中包装sql代码,无论它是什么。不需要游标。

begin;
    update t
    set qry_count = qry_count + 1
    from (
        select a
        from t
        where b = 'a'
        ) s
    where t.a = s.a
    ;
    select *
    from t
    where b = 'a'
    ;
commit;

答案 2 :(得分:1)

PostgreSQL UPDATE功能FROM子句,提供数据驱动的更新。例如:

UPDATE table1 t1 
SET blah = t2.c1 
FROM table2 t2 
WHERE t1.id = t2.t1id 
  

from_list :: =表表达式列表,允许列来自   其他表出现在WHERE条件和更新中   表达式。这类似于可以的表列表   在SELECT语句的FROM子句中指定。请注意   目标表不得出现在from_list中,除非您打算使用   自联接(在这种情况下,它必须出现在别名中   from_list)。

但是对于您的特定情况,使用WHERE ... IN ...进行简单更新可以解决问题:

UPDATE t
SET qry_count = qry_count + 1
WHERE entered_by IN ( SELECT entered_by FROM ... WHERE [your condition] )