Использование временной таблицы в процедуре 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 ... ]
TheLIKE
предложение указывает таблицу, из которой новая таблица автоматически копирует все имена столбцов, их типы данных и их ограничения not-null.
но я думаю, что вам просто нужно временная таблица для хранения некоторых идентификаторов, так что это, вероятно, излишне.
SELECT INTO
работает так, как вы ожидаете за пределами процедура:
[...] эта форма SELECT INTO недоступна в ECPG или PL/pgSQL, поскольку они интерпретируют предложение INTO по-разному.
SELECT INTO
используется для хранения результата выбора в локальной переменной внутри процедуры PostgreSQL:
в результат команды SQL, дающей одну строку (возможно, несколько столбцов), может быть назначен переменной записи, переменной типа строки или списку скалярных переменных. Это делается путем написания базовой команды SQL и добавления
INTO
предложения.
помимо явного создания временной таблицы, а затем вставки в нее, есть еще одна, более простая,право способ сделать это: CREATE TEMP TABLE AS
as рекомендуется в документах:
эта команда функционально похожа на
SELECT INTO
, но это предпочтительно, так как это менее вероятно спутать с другими видами использования theSELECT 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;