PostgreSQL:是否可以在单个事务中更改表并插入它?

coc*_*nup 3 postgresql transactions insert alter

我的用例如下:我正在定期将数据导入到 PostgreSQL 数据库,其中必须定期导入一些外部数据。

需要注意的是,数据的结构可能会从一个导入更改为另一个导入,因此在插入新数据之前,我会在每次导入时截断+删除所有列。

我想将整个操作包装在单个事务中,因此,如果出现问题,事务将回滚,并且旧数据仍然可以访问(两害相权取其轻)。

例如,数据导入语句可能如下所示:

BEGIN

ALTER TABLE "external_data" DROP "date"
ALTER TABLE "external_data" DROP "column1"
ALTER TABLE "external_data" DROP "column2"

ALTER TABLE "external_data" ADD "date" date DEFAULT NULL
ALTER TABLE "external_data" ADD "column1" text DEFAULT NULL
ALTER TABLE "external_data" ADD "column2" text DEFAULT NULL
ALTER TABLE "external_data" ADD "column3" text DEFAULT NULL

INSERT INTO "external_data" ("date","column1","column2","column3") VALUES ('20170523','Berlin','Chrome','1'),('20170524','Berlin','Chrome','2')

COMMIT
Run Code Online (Sandbox Code Playgroud)

目前这不起作用。INSERT 语句会被卡住,因为在调用它时,该表仍然被前面的 ALTER TABLE 语句锁定。

有什么方法可以在 Postgres 事务中实现这一点,还是我应该放弃并寻求其他应用程序端解决方案?

Dan*_*ité 5

目前这不起作用。INSERT 语句会被卡住,因为在调用它时,该表仍然被前面的 ALTER TABLE 语句锁定。

不,事务不能以这种方式锁定自身。如果 INSERT 是由另一个事务发起的,而不是由已经对该对象具有强锁的事务发起的,则该 INSERT 将被阻止。删除该列并在同一事务中执行后续 INSERT 没有问题。

正如评论中提到的,它似乎被卡住的原因可能是,如果将问题中的查询序列提供给交互式解释器,它根本不会执行任何查询,因为没有结束的指示任何查询。如果解释器是此序列,则查询末尾psql缺少分号或元命令。\g

SQL 查询本身不需要在末尾加分号,只有当多个查询可以一起提交时才需要分号。


Emi*_*zer 5

是的。可以更改表并在一个事务中插入

您可以在http://rextester.com/OTU89086中查看示例

但。警告您不能执行许多删除/添加列操作

您可以添加的列数是有限制的(即使您删除其他列)。如果你做了很多你会得到:

54011: tables can have at most 1600 columns
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到这个问题:

--PostgreSQL 9.6
--'\\' is a delimiter

select version() as postgresql_version;

drop table if exists "external_data";

create table "external_data"( 
    "date" date,
    "column1" integer,
    "column2" text,
    "column3" boolean
);

BEGIN TRANSACTION;

create or replace function do_the_import()
  returns text
  language plpgsql as 
$body$
begin
  ALTER TABLE "external_data" DROP "date";
  ALTER TABLE "external_data" DROP "column1";
  ALTER TABLE "external_data" DROP "column2";
  ALTER TABLE "external_data" DROP "column3";

  ALTER TABLE "external_data" ADD "date" date DEFAULT NULL;
  ALTER TABLE "external_data" ADD "column1" text DEFAULT NULL;
  ALTER TABLE "external_data" ADD "column2" text DEFAULT NULL;
  ALTER TABLE "external_data" ADD "column3" text DEFAULT NULL;

  INSERT INTO "external_data" ("date","column1","column2","column3") VALUES ('20170523','Berlin','Chrome','1'),('20170524','Berlin','Chrome','2');

  return current_timestamp::text;
end;
$body$;

select count(do_the_import()) from generate_series(1,1000);

COMMIT;
Run Code Online (Sandbox Code Playgroud)

在这里尝试一下: http: //rextester.com/RPER86062