PostgreSQL与Oracle默认事务管理

PostgreSQL与Oracle默认事务管理,oracle,postgresql,transactions,Oracle,Postgresql,Transactions,在PostgreSQL中,如果您在事务中遇到错误,例如,当insert语句违反unique约束时,整个事务将中止,您将无法提交它,并且不会插入任何行: database=# begin; BEGIN database=# insert into table (id, something) values ('1','whatever'); INSERT 0 1 database=# insert into table (id, something) values ('1','whatever');

在PostgreSQL中,如果您在事务中遇到错误,例如,当insert语句违反unique约束时,整个事务将中止,您将无法提交它,并且不会插入任何行:

database=# begin;
BEGIN
database=# insert into table (id, something) values ('1','whatever');
INSERT 0 1
database=# insert into table (id, something) values ('1','whatever');
ERROR:  duplicate key value violates unique constraint "table_id_key"
Key (id)=(1) already exists.
database=# insert into table (id, something) values ('2','whatever');
ERROR:  current transaction is aborted, commands ignored until end of transaction block
database=# rollback;
database=# select * from table;
id   | something  | 
-----+------------+

(0 rows)
您可以通过将ON\u ERROR\u ROLLBACK设置为ON或interactive来更改这一点,然后您可以忽略错误进行多次插入、提交并在事务结束后仅成功地在表中插入行

database=# \set ON_ERROR_ROLLBACK interactive
在Oracle中,这是默认的事务管理行为,这让我感到惊讶。这不是完全违反直觉和危险吗

启动事务时,我希望确保所有语句都已成功。如果我的多个插入包含某种对象或数据结构,该怎么办?我最终完全不知道数据库中的数据状态,应该在提交后检查它。 如果其中一个插入失败,我希望确保在第一个错误发生后,其他插入将被回滚,甚至不会被计算,这正是PostgreSQL中的做法

为什么Oracle有这样的事务管理方式作为默认,为什么它被认为是良好的做法

例如,某个随机的家伙

这是一个非常简洁的特性

但我不明白这一点:通常,你犯的任何错误都会 引发异常并将当前事务标记为 流产了。这是理智和预期的行为

不,真的不是。Oracle和MySQL都不是这样工作的。我 我没有使用MSSQL或DB2的经验,但我敢打赌他们每个人一美元 也不要这样工作。没有直观的理由可以解释为什么语法 错误或任何其他错误都应该中止事务。 我只能假设在博士后中存在着某种局限性 需要这种行为的勇气,或者说它符合某种模糊的规则 SQL标准的一部分,其他人都会理智地忽略它。有 当然,没有API/UX理由说明它应该以这种方式工作

我们真的不应该对我们开发的任何解决方案感到太骄傲 因为这种病态的行为。就像斯德哥尔摩综合症

它是否违反了交易的定义

交易提供了一个全有或全无的命题,表明 在数据库中执行的每个工作单元必须在其 全部或全部无效


我同意你的看法。我认为不中止整个tx是错误的。但是人们已经习惯了,所以他们认为这是合理和正确的。像使用MySQL的人一样,认为DBMS应该接受0000-00-00作为日期,或者使用Oracle的人希望它为空

语法错误和其他错误之间存在明显区别的想法是有缺陷的

如果我写

BEGIN;

CREATE TABLE new_customers (...);

INSET INTO new_customers (...)
SELECT ... FROM customers;

DROP TABLE customers;

COMMIT;
我不在乎是打字错误导致语法错误导致数据丢失。我关心的是,该事务没有成功执行其所有语句,但仍然提交

在语句实际写入任何行之前,在PostgreSQL中允许软回滚在技术上是可行的——甚至可能在我们进入执行器之前。因此,解析和参数绑定阶段的失败可能会导致发送无法中止。我们有一个语句内存上下文可以用来清理


但是,一旦语句开始更改行,它将在磁盘上使用与tx中先前语句相同的事务ID进行更改。因此,如果不回滚整个tx,则无法回滚。要允许语句回滚,Pg需要分配一个新的子事务ID。这会消耗资源。您可以在需要时显式地使用保存点,而在内部,psql就是这样做的。理论上,我们可以允许服务器隐式地为每个语句执行此操作,以实现语句回滚,而这只是以性能为代价。但我怀疑任何实现这一点的补丁都会被提交,至少不会没有太多的争论,因为PostgreSQL团队中的大多数人在我看来都不喜欢“呜呜”的叫声,这已经中断了,但我们无论如何都会继续进行。

我同意你的看法。我认为不中止整个tx是错误的。但是人们已经习惯了,所以他们认为这是合理和正确的。像使用MySQL的人一样,认为DBMS应该接受0000-00-00作为日期,或者使用Oracle的人希望它为空

语法错误和其他错误之间存在明显区别的想法是有缺陷的

如果我写

BEGIN;

CREATE TABLE new_customers (...);

INSET INTO new_customers (...)
SELECT ... FROM customers;

DROP TABLE customers;

COMMIT;
我不在乎是打字错误导致语法错误导致数据丢失。我关心的是,该事务没有成功执行其所有语句,但仍然提交

在语句实际写入任何行之前,在PostgreSQL中允许软回滚在技术上是可行的——甚至可能在我们进入执行器之前。因此,解析和参数绑定阶段的失败可能会导致发送无法中止。我们有一个语句内存上下文 我们可以用它来清理


但是,一旦语句开始更改行,它将在磁盘上使用与tx中先前语句相同的事务ID进行更改。因此,如果不回滚整个tx,则无法回滚。要允许语句回滚,Pg需要分配一个新的子事务ID。这会消耗资源。您可以在需要时显式地使用保存点,而在内部,psql就是这样做的。理论上,我们可以允许服务器隐式地为每个语句执行此操作,以实现语句回滚,而这只是以性能为代价。但我怀疑任何实现这一点的补丁都会被提交,至少不会没有太多的争论,因为PostgreSQL团队的大多数人都不喜欢“哎哟”,这是错误的,但无论如何我们都会继续使用事务语义。

说得好!也许有一些论点可以这样或那样做。只是我看不到任何东西,除了Oracle的便利性。使用Oracle的人希望NULL=NULL是真的而不是NULL,你确定吗?@KonstantinSorokin不,我错了。我想的是,在Oracle 9i中,是空的。我不记得是什么DBMS,其中NULL=NULL是真的。。。老SQL小姐?Microsoft Access/JET引擎?老MYSQL?说得好!也许有一些论点可以这样或那样做。只是我看不到任何东西,除了Oracle的便利性。使用Oracle的人希望NULL=NULL是真的而不是NULL,你确定吗?@KonstantinSorokin不,我错了。我想的是,在Oracle 9i中,是空的。我不记得是什么DBMS,其中NULL=NULL是真的。。。老SQL小姐?Microsoft Access/JET引擎?老MYSQL?