PostgreSQL:是否可以在单个事务中对其进行ALTER TABLE和INSERT?

时间:2017-07-02 15:04:23

标签: postgresql transactions insert alter

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

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

我想将整个操作包装在一个事务中,所以如果出现问题,事务将被回滚,旧的数据仍然可以访问(两种不同的东西)。< / p>

举个例子,这是数据导入语句的样子:

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

目前无效。 INSERT语句被卡住了,因为当它被调用时,表仍然被锁定在它之前的ALTER TABLE语句中。

在Postgres交易中有没有办法实现这一点,还是应该放弃并去寻求其他解决方案?

2 个答案:

答案 0 :(得分:3)

是。改变表并在一个事务中插入

是可行的

您可以在http://rextester.com/OTU89086

中看到该示例

但是。警告你不能做很多删除/添加列

您可以添加多少列(即使您丢弃其他列)也有限制。如果你做了很多,你会得到:

54011: tables can have at most 1600 columns

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

--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;

在此处试试:http://rextester.com/RPER86062

答案 1 :(得分:2)

  

目前无效。 INSERT语句卡住了   因为,当它被调用时,表仍然被ALTER锁定   在它之前的TABLE语句。

不,交易无法以这种方式锁定自己。如果INSERT是由另一个事务启动的,则会被阻塞,而不是已经对该对象进行强锁定的事件。删除列并在同一事务中执行后续INSERT没有问题。

如评论所述,它似乎陷入困境的原因可能是,如果您将问题的查询序列提供给交互式解释器,它根本不会执行任何查询,因为没有任何指示任何查询的结尾。如果解释器为psql,则此序列在查询结束时缺少分号或\g元命令。

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