Использование временной таблицы в процедуре PL / pgSQL для очистки таблиц

Я пытаюсь удалить все данные, связанные с идентификатором пользователя из базы данных игры.

существует таблица, содержащая все игры (в каждую играют 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

когда я пробую следующий оператор SELECT INTO в psql-prompt PostgreSQL, он, похоже, работает так, как ожидалось (и временная таблица исчезает при закрытии сеанса):

# 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", потому что я еще не привык к этому, и мои скрипты/база данных еще не готовы к этому).

3 ответов


вы можете создать временную таблицу, а затем сделать обычный 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 parent_table [ like_option ... ]
The LIKE предложение указывает таблицу, из которой новая таблица автоматически копирует все имена столбцов, их типы данных и их ограничения not-null.

но я думаю, что вам просто нужно временная таблица для хранения некоторых идентификаторов, так что это, вероятно, излишне.

SELECT INTO работает так, как вы ожидаете за пределами процедура:

[...] эта форма SELECT INTO недоступна в ECPG или PL/pgSQL, поскольку они интерпретируют предложение INTO по-разному.

SELECT INTO используется для хранения результата выбора в локальной переменной внутри процедуры PostgreSQL:

в результат команды SQL, дающей одну строку (возможно, несколько столбцов), может быть назначен переменной записи, переменной типа строки или списку скалярных переменных. Это делается путем написания базовой команды SQL и добавления INTO предложения.


помимо явного создания временной таблицы, а затем вставки в нее, есть еще одна, более простая,право способ сделать это: CREATE TEMP TABLE AS as рекомендуется в документах:

эта команда функционально похожа на SELECT INTO, но это предпочтительно, так как это менее вероятно спутать с другими видами использования the 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 или более поздней использовать изменение данных CTEs для простых случаев, как один под рукой:

   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;

вы можете попробовать

EXECUTE 'create temp table temp_gids AS select from pref_scores where id='
    USING _id;