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

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

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

Почему это (временные таблицы здесь не разрешены?) И где сохранить мой временный список гидов, которые нужно удалить?

(И я бы предпочел не использовать «каскад удаления», потому что я 'я к этому еще не привык, и мои сценарии / база данных еще не готовы к этому).

Ответы [ 3 ]

14 голосов
/ 30 октября 2011

Помимо явного создания временной таблицы и последующей вставки в нее, есть еще один, более простой правильный способ сделать это: 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;
9 голосов
/ 28 октября 2011

Вы можете создать временную таблицу и затем выполнить обычные 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 для CREATE TABLE , если вы хотите продублировать структуру таблицы:

LIKE parent_table [ like_option ... ]
Предложение LIKE указывает таблицу, из которой новая таблица автоматически копирует все имена столбцов, их типы данных и ограничения на ненулевое значение.

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

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

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

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

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

1 голос
/ 29 октября 2011

Можно попробовать

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