作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
列昂尼德·德拉金斯基的头像

列昂尼德•Draginsky

验证专家 in 工程

Leonid有20多年的多平台开发和DBA经验, 在Oracle和SQL Server跨平台迁移方面具有特殊的专业知识.

以前在

J.P. Morgan Chase)
分享

“供应商依赖”对许多业务主管来说是一个可怕的词. 另一方面, 业界已经普遍认识到,完全的“供应商独立”是不可能实现的. 对于数据库来说尤其如此.

两个最普及的企业RDBMS平台是Oracle数据库和Microsoft SQL Server(简单地说), 对于本文的其余部分, 我将分别称之为“Oracle”和“SQL Server”。). 确定, IBM Db2与Oracle在大型机平台上的竞争日益缩小,但在许多领域仍然至关重要. 以及快速发展的开源替代品, 例如PostgreSQL, 在中低层次的商品硬件和网络的动态环境中获得了稳固的基础吗.

甲骨文与. SQL Server 是许多业务主管在他们的组织需要一个新的RDBMS时所面临的选择. 最终的选择是基于多个因素:许可证成本, 具备内部专业知识和过去的经验, 与现有环境的兼容性, 合作伙伴关系, 未来的商业计划等. 但即使有最彻底的前期评估和最明智的决策, 有时候因素改变了,平台也需要改变, 太. 我知道这一点,因为在我的职业生涯中, 我已经实现过两次这样的迁移, 编写一次转型可行性评估, 我现在正致力于跨平台功能迁移.

Oracle和SQL Server都是“老派的”、部分符合ansi的RDBMS实现. 当不考虑过程扩展时,pl /SQL和transact - sql具有不同的语法, 但是通常很容易在面向对象的未来和新的面向对象之间进行转换, SQL代码可能看起来很相似. 这是一个危险的美人计.

的两个 任何移植项目的最关键的点 在Oracle和SQL Server之间(在任何方向上) 交易 与此密切相关的是, 临时表,这是解决事务范围的关键工具. 我们还将介绍嵌套事务(存在于另一个事务范围内的事务),因为它们是在Oracle中实现用户安全审计的关键部分. 但是在SQL Server中,用户安全审计需要一种不同的方法 提交 在这种情况下的行为.

理解事务结构:观察Oracle和Oracle. 从一万英尺的SQL服务器

Oracle事务是隐式的. 这意味着您不需要启动事务—您始终处于事务中. 在发出提交或回滚语句之前,该事务一直处于打开状态. 是的, 您可以显式地启动事务, 定义回滚安全点, 和 set inner/nested 交易; but what’s important is that you’re never “not in a 事务” 和 you must always issue a commit or rollback. 还要注意,发出数据定义语言(DDL)语句(创建, 改动等.; in a 事务 it can be done through dynamic SQL) commits 的 事务 in which it was issued.

与Oracle不同,SQL Server有显式事务. 这意味着,除非您显式地启动一个事务, 当你的语句被处理时,你所有的更改都会“自动”提交, 作为每条DML语句(插入, 更新, 删除)自己创建事务并提交,除非出错.

这是数据存储实现差异的结果——如何将数据写入数据库以及数据库引擎如何读取数据.

在Oracle中,DML语句直接修改数据文件中的记录. 记录的旧副本(或空记录替换,在 插入)被写入当前回滚文件,并在记录上标记更改的确切时间.

当一个 SELECT 语句发出后,它将根据发出前已修改的数据进行处理. 是否有任何记录被修改 SELECT , Oracle使用回滚文件中的旧版本.

这就是Oracle实现读一致性和非阻塞读/写的方式. 这也是为什么在非常活跃的事务性数据库上长时间运行的查询有时会遇到臭名昭著的ORA-01555错误, 快照太老:回滚段 ... 太小了. (这意味着查询旧版本记录所需的回滚文件 已经被重复利用了.)这就是为什么“我的Oracle事务应该有多长??是“只要需要,不再需要。.”

SQL Server的实现是不同的:数据库引擎只直接向数据文件写入和读取. 每条SQL语句(SELECT/插入/更新/删除)是一个事务,除非它是将多个语句组合在一起的显式事务的一部分, 允许回滚更改.

每个事务都锁定它所需要的资源. 当前版本的Microsoft SQL Server在锁定所需资源方面进行了高度优化, 但需要什么是由SQL代码定义的,所以优化你的查询是至关重要的). 也就是说, 不像Oracle, SQL Server中的事务应该尽可能短, 这就是为什么自动提交是默认行为的原因.

在Oracle和SQL Server中,事务实现的差异会影响SQL结构的选择? 临时表.

Oracle和SQL Server中的临时表

当ANSI SQL标准定义本地和全局临时表时, 它没有明确说明应该如何实现它们. Oracle和SQL Server都实现了全局临时表. SQL Server也实现了本地临时表. Oracle 18c还实现了“真正的”本地临时表(他们称之为“私有临时表”).)这使得SQL Server代码到Oracle 18c的翻译明显比旧版本更简单——将Oracle的代码四舍五入 早期添加了一些相关功能 比如自动递增的标识列.

但是从纯功能分析的角度来看, 私有临时表的引入有利有弊,因为它使SQL Server到Oracle的迁移问题看起来没有那么严重. 这是另一个美人计,因为它可能会带来一些新的挑战. 例如, 设计时代码验证不能在私有临时表上进行, 因此,任何使用它们的代码总是更容易出错. 如果您使用过动态SQL, 让我们这么说吧:私有临时表的调试同样复杂, 但是没有明显的独特用例. 这就是为什么Oracle只在18c中添加了本地(私有)临时表,而不是以前.

简而言之, 我没有看到私有临时表在Oracle中的用例不能使用全局临时表实现相同或更好. 对于任何严肃的转换, 我们需要了解Oracle和SQL Server全局临时表之间的区别.

Oracle和SQL Server中的全局临时表

Oracle全局临时表是在设计时由DDL语句显式创建的永久数据字典对象. 它是“全局的”,因为它是一个数据库级对象,任何具有所需权限的数据库会话都可以访问它. 然而,尽管…… 结构 全球化,所有 data 在全局临时表中,它的作用域仅局限于它所操作的会话,而不是会话, 在任何情况下, 在此会话之外可见. 换句话说, 其他会话可以在同一全局临时表的副本中拥有自己的数据. So, 在Oracle中, 全局临时表保存会话本地数据,主要用于PL/SQL代码简化和性能优化.

在SQL Server中, 全局临时表是在transact - sql代码块中创建的临时对象. 只要它的创建会话是打开的,它就存在, 并且它对数据库中的其他会话是可见的(无论是结构还是数据). 它是一个全局临时对象用于跨会话共享数据.

SQL Server中的本地临时表与全局临时表的不同之处在于,只能在创建它的会话中访问. 在SQL Server中,本地临时表的使用更为广泛, 我会说, 更重要的是 数据库性能),而不是使用全局临时表.

那么,如何在SQL Server中使用本地临时表,以及如何将它们转换到Oracle中?

在SQL Server中,本地临时表的关键(也是正确的)用途是缩短或删除事务资源锁, 特别是:

  • 当需要通过聚合处理一组记录时
  • 当一组数据需要分析和修改时
  • 当同一组数据需要在同一作用域中多次使用时

在这些情况下, 通常,更好的解决方案是将这组记录选择到本地临时表中,从而从源表中删除锁.

值得注意的是,公共表表达式(cte, i.e. WITH AS (SELECT...) 语句)在SQL Server中只是“语法糖”。. 它们在SQL执行之前被转换为内联子查询. Oracle cte(带 /*+ materialize */ (提示)是性能优化的,并创建一个物化视图的临时版本. 在Oracle的执行路径中,cte只访问源数据一次. 基于这种差异, 使用本地临时表而不是对同一个CTE的多个引用,SQL Server的性能可能会更好, 就像在Oracle查询中一样.

因为事务实现之间的差异, 临时表还有一个不同的功能. 结果是, 将SQL Server临时表“按原样”移动到Oracle(即使使用Oracle 18c的私有临时表实现)不仅会损害性能, 但是功能上是错误的.

另一方面,当从Oracle迁移到SQL server时,需要注意事务长度, 全局临时表的可见性范围, 和 表演 带有“物化”提示的CTE块.

这两种情况, 一旦迁移的代码包含临时表, 我们不应该谈论代码翻译, 而是关于系统的重新实现.

输入表变量

开发人员可能会想:表变量怎么样? 我们是否需要进行任何更改,或者我们可以在oracle到sql - server迁移步骤中“按原样”移动表变量? 嗯,这取决于为什么以及如何在代码中使用它们.

让我们看看如何使用临时表和表变量. 我将从Microsoft SQL Server开始.

transact - sql中表变量的实现在某种程度上与临时表匹配,但增加了一些自己的功能. 关键的区别在于将表变量作为参数传递给函数和存储过程的能力.

这是理论上的,但实际使用的考虑更复杂一些.

当我来自一个根深蒂固的Oracle背景时,我第一次接受了transact - sql优化的任务, 我希望事情是这样的: 表变量 在内存中 临时表 在磁盘上. 但我发现微软SQL Server版本到2014年没有存储表变量在内存中. 因此,对临时变量的全表扫描实际上是对磁盘的全表扫描. 值得庆幸的是, SQL Server 2017及更高版本支持临时表和表变量的声明式内存优化.

So, 在transact - sql表变量的用例是什么,如果一切都可以做得很好或更好使用临时表? a的关键性质 表变量 这是一个 变量 因此不受事务回滚的影响,可以作为参数传递.

transact - sql 功能 都是非常严格的:作为一个任务 函数 是返回一个奇异的返回值,它是由设计的不能有副作用. transact - sql甚至可以看到 SELECT 作为一个副作用,因为在SQL Server 任何 对表的访问创建隐式事务和关联的事务锁. 这意味着在函数内部, 我们不能访问现有临时表中的数据, 也不创建临时表. 因此,如果我们需要将任何一组记录传递到函数中,我们可以 使用表变量.

Oracle使用(全局)临时表和 集合变量 (Oracle PL/SQL相当于transact - sql表变量)是不同的. Oracle集合变量在内存中,而 临时表 是否位于临时表空间中. Oracle 功能 allow read-only access to tables, permanent or temporary; a simple SELECT 在Oracle中从不对资源进行锁定.

在Oracle中,选择使用集合变量vs. 临时表基于预期的数据量, 需要保存此数据的持续时间, 记忆vs。. 磁盘分配和可用性. 此外,集合变量是将行集作为输出返回给主机程序的标准方法.

因为SQL Server和Oracle之间的大多数SQL语法元素看起来非常相似, 将带有表变量的代码块从SQL Server transact - sql转换为Oracle PL/SQL是一种更简单、语法更宽松的过程. 它可以通过基本的验证测试, 但是,除非采取临时表重新实现步骤,否则在功能上是不正确的, 如上所述. 另一方面, 从Oracle迁移到SQL Server的代码需要更多的修改步骤才能在语法上有效. 在功能上也是正确的, 它将需要解决使用临时表和cte的深入案例.

内部事务(“嵌套事务”)

在Oracle到SQL Server迁移方面的挑战, 下一个要看的主要领域是嵌套事务.

与临时表一样,如果transact - sql代码包含 任何 事务, 是否嵌套, 或Oracle代码包括任何嵌套的事务, 我们讨论的不仅仅是简单的代码迁移, 但是功能的重新实现.

首先,让我们看看Oracle嵌套事务的行为以及我们倾向于如何使用它们.

Oracle中的嵌套事务

Oracle嵌套事务是完全原子的,独立于外部作用域. 在普通交互式Oracle SQL查询中,嵌套事务没有实际用途. 当你在交互模式下使用Oracle时,当你看到你进入一个状态时,你只需要手动提交你的更改. 如果你做了一些改变,但你还不能提交,直到你做最后的决定, 不确定的步骤,可能需要回滚, 但是你想保留你已经做过的功, 您将创建一个安全点,以便在不提交或回滚完整事务的情况下回滚到它.

那么,在哪里使用嵌套事务? 在PL/SQL代码. 更具体地说 自动程序-申报有 编译指示AUTONOMOUS_TRANSACTION. 这意味着当调用该代码时(作为命名存储过程或匿名),事务的提交或回滚独立于调用该代码的事务.

使用嵌套事务的目标是,无论调用代码发生什么情况,都要提交或回滚一个自包含的工作单元. 何时可以提交内部事务 or 回滚后,它将用于检查共享资源的可用性(或保留).g. 在实施房间预订系统. 仅提交内部事务的主要用途是活动监视, 跟踪代码, 安全访问审计(i.e.,用户不允许进行更改,但试图进行更改.)

SQL Server transact - sql代码中的嵌套事务是完全不同的.

嵌套事务在SQL Server

在transact - sql, 内部事务是否完全提交取决于最外层的事务. 如果内部事务已经回滚,那么它只是回滚而已. 但是,如果内部事务已经提交,则仍然没有提交 完全 已提交,因为如果其外部范围事务的任何级别被回滚,则可以回滚.

So, 如果内部事务的提交可以通过回滚外部事务来撤销,那么内部事务还有什么用呢? 答案与本地临时表的用例相同:释放对资源的锁. 不同之处在于它不是一个全局锁释放, 而是直接外部(直接“父”)事务范围内的锁. 它在复杂的transact - sql代码中用于为外部事务释放内部资源. 它是一个性能优化和资源管理工具.

由于Oracle和SQL Server内部/嵌套事务具有不同(甚至相反)的行为和完全不同的用例, 从一个平台迁移到另一个平台不仅仅需要重写, 但是要完全重新架构任何包含嵌套事务块的作用域.

其他因素

这些以临时表和事务为中心的考虑是Oracle到SQL Server迁移中唯一需要解决的问题吗? 虽然它们可能是最重要的, 肯定还有其他的, 每个人都有自己的怪癖,值得报道. 以下是我发现的最容易被误解的话题:

  1. SQL Server中的标识列
  2. Oracle中的序列
  3. Oracle同义词
  4. 过滤索引
  5. 读一致性(仅限Oracle到SQL Server)
  6. 使用迁移工具

本系列的下一部分 继续探索这些,特别是前三个.

临时表、表/集合变量和嵌套事务:迁移的三大痛点

我从临时表开始, 表变量/集合, 以及嵌套事务,因为这些是转换项目中最常见和最明显的失败点. 任意非平凡系统 Oracle数据库 or Microsoft SQL Server 毫无疑问会使用其中的一些吗, 并且使用这些元素与各自RDBMS实现的事务支持的特定设计紧密耦合.

微软金牌合作伙伴徽章. (Toptal是微软的金牌合作伙伴.)

了解基本知识

  • Oracle和SQL Server有什么区别?

    Oracle和SQL Server都是部分ansi兼容的, 但是他们对标准的实现是不同的, 最明显的是交易结构. 在Oracle和Microsoft SQL Server之间迁移数据和应用程序是一个复杂的过程,需要对这两个平台有深入的了解.

  • 如何创建从Oracle到SQL Server的数据库链接?

    而创建从一个Oracle实例到另一个实例的数据库链接是标准功能, 创建从Oracle数据库到Microsoft SQL Server的链接需要异构服务和一个额外的许可证——它不是Oracle企业许可证的一部分.

  • 什么是Oracle事务?

    一个Oracle事务是一定量的工作(通过插入修改数据), 更新, 删除),可以永久应用(通过提交命令)到数据库.

  • 什么是SQL事务?

    SQL事务是一组数据操作,后面跟着提交或ROLLBACK语句. 特定的数据库实现在如何支持事务以及什么是“工作”方面存在差异.例如,Microsoft SQL Server将SELECT计算为事务的一部分,而Oracle不这样做.

  • 在Oracle中,提交和回滚哪个更快?

    在Oracle中,提交比ROLLBACK(快得多).

  • 在Oracle中提交后是否可以回滚?

    提交不能被“撤消”,,但Oracle确实支持“闪回”查询, 哪个可以获取过去某个时刻的数据. 使用闪回需要启用它的数据库配置. 闪回通常由dba用于从人为错误中恢复,很少作为应用程序功能的一部分.

  • 什么是Oracle中的临时表?

    Oracle中的临时表是临时表空间中的永久表对象, 保持临时数据仅在插入它的事务中可见. 不同的事务可以使用同一个临时表,但不能看到彼此的数据.

  • 临时表和变量表的区别是什么?

    SQL Server的临时表是在tempdb数据库中创建的, 而表变量是可以作为参数传递的内存对象(由" tempdb "支持), 就像普通变量一样. SQL Server 2017, 表变量获得了临时表的更多特性, 比如拥有索引的能力.

就这一主题咨询作者或专家.
预约电话
列昂尼德·德拉金斯基的头像
列昂尼德•Draginsky
验证专家 in 工程

位于 韦斯特维尔,俄亥俄州,美国

成员自 2020年6月18日

作者简介

Leonid有20多年的多平台开发和DBA经验, 在Oracle和SQL Server跨平台迁移方面具有特殊的专业知识.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

以前在

J.P. Morgan Chase)

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.