11

我正在尝试从游戏数据库中删除与用户 ID 相关的所有数据。

有一张桌子,里面放着所有游戏(每个游戏由 3 名玩家玩):

# select * from pref_games where gid=321;
 gid | rounds |          finished
-----+--------+----------------------------
 321 |     17 | 2011-10-26 17:16:04.074402
(1 row)

并且有一个表格包含该游戏#321的玩家得分:

# select * from pref_scores where gid=321;
      id       | gid | money | quit
----------------+-----+-------+------
 OK531282114947 | 321 |   218 | f
 OK501857527071 | 321 |  -156 | f
 OK429671947957 | 321 |   -62 | f

当我在 PostgreSQL 的 psql-prompt 上尝试以下 SELECT INTO 语句时,它似乎按预期工作(并且会话关闭时临时表消失):

# select gid into temp temp_gids from pref_scores where id='OK446163742289';
SELECT

# select * from temp_gids ;
 gid
------
 1895
 1946
 1998
 2094
 2177
 2215
(6 rows)

但是当我尝试创建我的 PL/pgSQL 过程时,我得到了错误:

    create or replace function pref_delete_user(_id varchar)
        returns void as $BODY$
            begin

            select gid into temp temp_gids from pref_scores where id=_id;
            delete from pref_scores where gid in
                (select gid from temp_gids);
            delete from pref_games where gid in
                (select gid from temp_gids);

            delete from pref_rep where author=_id;
            delete from pref_rep where id=_id;

            delete from pref_catch where id=_id;
            delete from pref_game where id=_id;
            delete from pref_hand where id=_id;
            delete from pref_luck where id=_id;
            delete from pref_match where id=_id;
            delete from pref_misere where id=_id;
            delete from pref_money where id=_id;
            delete from pref_pass where id=_id;
            delete from pref_status where id=_id;
            delete from pref_users where id=_id;

            end;
    $BODY$ language plpgsql;

错误:

ERROR:  syntax error at "temp"
DETAIL:  Expected record variable, row variable, or list of scalar variables following INTO.
CONTEXT:  compilation of PL/pgSQL function "pref_delete_user" near line 3

为什么会这样(这里不允许使用临时表?)以及在哪里保存要删除的 gid 的临时列表?

(而且我不想使用“on delete cascade”,因为我还不习惯它,而且我的脚本/数据库还没有为此做好准备)。

4

3 回答 3

15

除了显式创建一个临时表然后插入其中之外,还有另一种更简单、正确的方法:CREATE TEMP TABLE AS按照文档中的建议

此命令在功能上类似于,但它是首选,因为它不太可能与语法SELECT INTO的其他用途混淆。SELECT INTO此外,CREATE TABLE AS还提供了SELECT INTO.

对于 Postgres 9.1 或更高版本,请参见下文。

DELETE .. USING ..使用而不是子选择也会更有效。
是的,如果您不打算在事务提交后继续使用临时表(在同一个会话中),请添加ON COMMIT DROP.

综上所述,您的函数可能如下所示:

CREATE OR REPLACE FUNCTION pref_delete_user(_id varchar)
  RETURNS void AS
$func$
BEGIN    
   CREATE TEMP TABLE tmp_gids ON COMMIT DROP AS
   SELECT gid FROM pref_scores WHERE id = _id;

   DELETE FROM pref_scores p
   USING  tmp_gids t
   WHERE  p.gid = t.gid;

   DELETE FROM pref_games p
   USING  tmp_gids t
   WHERE  p.gid = t.gid;

   -- more deletes ...    
END
$func$ LANGUAGE plpgsql;

数据修改 CTE

在现代 Postgres 中,上述内容仅适用于需要使用实际临时表的复杂操作 - 例如,在继续之前在其上创建索引。

在 Postgres 9.1 或更高版本中,将数据修改 CTE用于手头的简单情况:

   WITH gids AS (SELECT gid FROM pref_scores WHERE id = _id)
      , d1   AS (  
      DELETE FROM pref_scores p
      USING  gids t
      WHERE  p.gid = t.gid
      (
      -- more work using gids?
   DELETE FROM pref_games p
   USING  gids t
   WHERE  p.gid = t.gid;
于 2011-10-29T22:52:14.973 回答
13

您可以创建临时表,然后执行通常INSERT ... SELECT的单独操作:

create temporary table temp_gids (gid int not null) on commit drop;
insert into temp_gids (gid) select gid from pref_scores where id = _id;

如果你想复制一个表的结构,还有一个LIKE 选项可以创建 TABLE :

LIKE parent_table [ like_option ... ]
LIKE子句指定一个表,新表会自动从该表中复制所有列名、它们的数据类型和它们的非空约束。

但我认为你只需要一个临时表来保存一些 ID,所以这可能是矫枉过正。

SELECT INTO在程序之外按您期望的方式工作:

[...] 这种形式的 SELECT INTO 在 ECPG 或 PL/pgSQL 中不可用,因为它们对 INTO 子句的解释不同。

SELECT INTO用于将 SELECT 的结果存储在PostgreSQL 过程中的局部变量中:

产生单行(可能是多列)的 SQL 命令的结果可以分配给记录变量、行类型变量或标量变量列表。这是通过编写基本 SQL 命令并添加一个INTO子句来完成的。

于 2011-10-28T18:24:43.167 回答
1

你可以试试

EXECUTE 'create temp table temp_gids AS select from pref_scores where id=$1'
    USING _id;
于 2011-10-28T20:13:59.503 回答