Postgres中从CTE或TEMPORARY TABLE进行第二次读取后出现错误。

huangapple go评论61阅读模式
英文:

Error after second read from CTE or TEMPORARY TABLE in Postgres

问题

I'm your Chinese translator, I will only provide the translated text without additional content:

我正在尝试编写一个查询,在该查询中,我通过某些条件选择记录,更新这些记录的一个列,然后在一个事务中将它们返回。

我尝试了两个选项。首先是使用CTE:

BEGIN;
WITH unwatched_rows AS (
        SELECT * FROM my_table WHERE is_readed = false FOR UPDATE
)
UPDATE my_table SET is_readed = true WHERE id IN (SELECT id FROM unwatched_rows);
SELECT * FROM unwatched_rows;
COMMIT;

在这种情况下,我得到一个错误:“关系“unwatched_rows”不存在”。我了解到这是因为我不能多次使用CTE。据我了解,CTE不适用于我的情况。

第二个选项是使用“CREATE TEMPORARY TABLE”:

BEGIN;
CREATE TEMPORARY TABLE unwatched_rows AS (
        SELECT * FROM my_table WHERE is_readed = false FOR UPDATE
);
UPDATE my_table SET is_readed = true WHERE id IN (SELECT id FROM unwatched_rows);
SELECT * FROM unwatched_rows;
COMMIT;

我在DBeaver中运行这个查询。第一次运行时,它正常工作,但当我尝试再次运行时,我得到一个错误:“关系“unwatched_rows”已经存在”。我不明白为什么会发生这种情况。临时表不应该在提交后被删除吗?我做错了什么,如何解决这个问题?

英文:

I am trying to write a query where I select records by some condition, update one column of those records and then return them, all in one transaction.

I have tried two options. First with CTE:

BEGIN;
WITH unwatched_rows AS (
        SELECT * from my_table WHERE is_readed = false FOR UPDATE
)
UPDATE my_table SET is_readed = true WHERE id IN (SELECT id FROM unwatched_rows);
SELECT * FROM unwatched_rows;
COMMIT;

In this case I get an error relation "unwatched_rows" does not exist. How I learned this is because i can't use CTE more than once. And as I understand it, CTE is not suitable for my situation

Second option with CREATE TEMPORARY TABLE:

BEGIN;
CREATE TEMPORARY TABLE unwatched_rows AS (
        SELECT * from my_table WHERE is_readed = false FOR UPDATE
);
UPDATE my_table SET is_readed = true WHERE id IN (SELECT id FROM unwatched_rows);
SELECT * FROM unwatched_rows;
COMMIT;

I am running this query through DBeaver. For first time it works fine, but when I try to run this query again I get an error relation "unwatched_rows" already exists. I don't understand why this is happening. Shouldn't the temporary table be dropped right after the commit?
What am I doing wrong and how to solve it?

答案1

得分: 1

如果您希望在提交后立即删除临时表,您需要在创建时指定 ON COMMIT DROP

BEGIN;
CREATE TEMPORARY TABLE unwatched_rows ON COMMIT DROP AS (
        SELECT * from my_table WHERE is_readed = false FOR UPDATE
);
UPDATE my_table SET is_readed = true WHERE id IN (SELECT id FROM unwatched_rows);
SELECT * FROM unwatched_rows;
COMMIT;

另一种解决方案是在重新创建临时表之前删除它:

BEGIN;
DROP TABLE IF EXISTS unwatched_rows;
CREATE TEMPORARY TABLE unwatched_rows AS (
        SELECT * from my_table WHERE is_readed = false FOR UPDATE
);
UPDATE my_table SET is_readed = true WHERE id IN (SELECT id FROM unwatched_rows);
SELECT * FROM unwatched_rows;
COMMIT;
英文:

If you want the temporary table to be removed right after the commit, You will have to specify ON COMMIT DROP when create it :

BEGIN;
CREATE TEMPORARY TABLE unwatched_rows ON COMMIT DROP AS (
        SELECT * from my_table WHERE is_readed = false FOR UPDATE
);
UPDATE my_table SET is_readed = true WHERE id IN (SELECT id FROM unwatched_rows);
SELECT * FROM unwatched_rows;
COMMIT;

An other solution is to drop your temporary table before created it again :

BEGIN;
DROP TABLE IF EXISTS unwatched_rows;
CREATE TEMPORARY TABLE unwatched_rows AS (
        SELECT * from my_table WHERE is_readed = false FOR UPDATE
);
UPDATE my_table SET is_readed = true WHERE id IN (SELECT id FROM unwatched_rows);
SELECT * FROM unwatched_rows;
COMMIT;

答案2

得分: 1

You can certainly use a CTE (with ...) once. However you used it incorrectly. A CTE is essentially a view which exists only for the duration of the query where it is created. In query the semi-colon (;) after the update terminated the statement and thus the CTE itself goes out-of-scope. So for the select statement it no longer exists. Postgres allows DML in a CTE so to correct your statement create a second CTE that does the update. So:

使用CTE(with ...)是可以的,但是您使用它不正确。CTE本质上是一个视图,仅在创建它的查询持续时间内存在。在update之后的分号(;)终止了该语句,因此CTE本身超出了作用范围。因此,对于select语句,它不再存在。Postgres允许在CTE中使用DML,因此要纠正您的语句,创建第二个执行更新的CTE。因此:

with unwatched_rows as (
    select id 
    from my_table 
    where is_readed = false for update
)
, upt as (
    update my_table 
    set is_readed = true 
    where id in (select id from unwatched_rows)
) 
select * 
from unwatched_rows;

As far as wrapping into a function this is actually quite simple. It does not require a temp table not, for that matter, a CTE. It reduces to a single SQL statement:

至于将其封装为函数,这实际上非常简单。它不需要临时表,也不需要CTE。它可以简化为一个SQL语句:

create or replace function unwatched_rows()
  returns setof my_table.id%type 
 language sql 
as $$ 
    update my_table 
    set is_readed = true
    where not is_readed
    returning id;
$$;

See here for demo illustrating both.

请参阅此处以查看演示,其中说明了这两种方法。

英文:

You can certainly use a CTE (with ...) once. However you used it incorrectly. A CTE is essentially a view which exists only for the duration of the query where it is created. In query the semi-colon (;) after the update terminated the statement and thus the CTE itself goes out-of-scope. So for theselect statement it no longer exists. Postgres allows DML in a CTE so to correct your statement create a second CTE that does the update. So:

with unwatched_rows as (
        select id 
          from my_table 
         where is_readed = false for update
     )
   , upt as (
       update my_table 
          set is_readed = true 
        where id in (select id from unwatched_rows)
     ) 
select * 
  from unwatched_rows;

As far as wrapping into a function this is actually quite simple. It does not require a temp table not, for that matter, a CTE. It reduces to a single SQL statement:

create or replace function unwatched_rows()
  returns setof my_table.id%type 
 language sql 
as $$ 
    update my_table 
       set is_readed = true
     where not is_readed    -- same as: is_readed = false
    returning id;
$$;

See here for demo illustrating both.

huangapple
  • 本文由 发表于 2023年4月17日 19:18:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76034575.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定