【问题标题】:How should I handle implicit transactions for a SQL Server stored procedure being called from an Oracle database?我应该如何处理从 Oracle 数据库调用的 SQL Server 存储过程的隐式事务?
【发布时间】:2025-12-27 21:40:06
【问题描述】:

我正在使用数据库链接从 Oracle 数据库调用 SQL Server 存储过程。存储过程完成后,隐式事务保持打开状态。 Oracle 文档在此处记录了这一点:http://docs.oracle.com/cd/E18283_01/gateways.112/e12069/ch3.htm#insertedID8 并声明,

“网关支持 ANSI 标准的隐式事务。必须为此模式编写 SQL Server 存储过程。”

我是否应该将其理解为 SQL Server 存储过程应在过程中的某个位置包含不匹配的 COMMIT 语句?这是否比要求调用者从调用方以显式 COMMIT 语句结束(即后一种方法是否会导致孤立的分布式事务)更好?

【问题讨论】:

    标签: sql sql-server oracle stored-procedures


    【解决方案1】:

    您引用的文章提到:

    “运行隐式事务允许网关将 Oracle 两阶段提交保护扩展到更新 Oracle 和 SQL Server 数据库的事务”。

    “两阶段提交保护”意味着如果您在 Oracle 连接中打开了一个事务,那么即使 SQL Server proc 调用“commit”,Oracle Database Gateway for SQL Server 也会在 SQL 上保持该事务处于打开状态服务器端,直到在 Oracle 端运行的事务被提交(或回滚)。如果在原始事务中发生提交(在 Oracle 中启动的事务),则应提交对 SQL Server 的相应更新,对于回滚也是如此。所以基本上,在 Oracle 执行您的过程之前,它会在 SQL Server 端调用 BEGIN TRANSACTION,然后调用该过程的执行,然后在事务的其余部分执行它需要的任何其他操作(可能是 Oracle 端的更新或SQL Server 端),然后只有从 Oracle 端调用 COMMIT 时,网关才会将提交命令发送回 SQL Server。

    因此,需要调用者 (Oracle) 以显式 COMMIT 结束的后一个选项。

    如果您在 SQL Server 过程中添加了不匹配的“提交”,则 SQL Server 可能会抛出以下错误:

    消息 266,级别 16,状态 2,过程 YOUR_PROC_NAME,第 0 行 EXECUTE 之后的事务计数表明 BEGIN 和 COMMIT 语句的数量不匹配。先前计数 = 1,当前计数 = 0。

    此外,您对无法正常结束(并且可能最终成为孤立)的连接的担忧是一个有效的担忧,并且您正在考虑这种情况是件好事。如果与 SQL Server 的连接中断,则 SQL Server 将在连接关闭时回滚打开的事务。在此处查看“事务处理期间的错误”部分:http://msdn.microsoft.com/en-us/library/ms175523%28v=sql.105%29.aspx

    如果客户端与数据库引擎实例的网络连接中断,则当网络通知实例中断时,该连接的所有未完成事务都会回滚。如果客户端应用程序失败或者客户端计算机出现故障或重新启动,这也会中断连接,并且数据库引擎实例会在网络通知它中断时回滚所有未完成的连接。如果客户端注销应用程序,所有未完成的事务都会回滚。

    如果由于某种原因,Oracle 数据库网关会在发生错误后打开分布式事务的连接而没有显式提交或回滚,那么您可能会遇到保持打开状态的事务,但我不希望发生这种情况因为那会破坏分布式事务的目的。

    【讨论】:

    • 我已经尝试了这两个选项并验证它们都有效。我担心的是,如果 Oracle 代码打开一个事务,获取一些资源,然后连接不正常地结束,这些资源可能会保持锁定一段时间。我可以确保资源被释放的唯一方法是在 SQL Server 端提交事务,但这似乎不是一个干净的解决方案。
    • 在 SQL proc 中使用不匹配的COMMIT 有效吗?这令人惊讶... proc 的结构是否类似于 CREATE PROC myProc BEGIN ...some DML operations here... COMMIT /*with no explicit BEGIN TRAN?*/ END