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

PostgreSQL: is it possible to ALTER TABLE and INSERT to it within a single transaction?

我的用例如下:我正在将定期数据导入到 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

这目前无法正常工作。 INSERT 语句卡住了,因为在调用它时,table 仍然被前面的 ALTER TABLE 语句锁定。

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

This is currently not working. The INSERT statement gets stuck because, when it's called, the table is still locked from the ALTER TABLE statement that preceded it.

不,事务不能那样锁定自己。如果 INSERT 是由另一个事务发起的,而不是由已经对该对象具有强锁定的事务发起的,INSERT 将被阻塞。删除该列并在同一事务中执行后续 INSERT 没有问题。

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

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

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

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

中查看示例

但是。警告你不能做很多 drop/add 列

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

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