【问题标题】:Moving TIMESTAMP column from Oracle to SQL Server将 TIMESTAMP 列从 Oracle 移动到 SQL Server
【发布时间】:2019-02-07 14:42:44
【问题描述】:

我有一个 Oracle 数据库,其中包含一些我想要移动到 SQL Server 的数据。

问题是我的 Oracle DB 有一些类型为 TIMESTAMP(0) WITH TIME ZONE 的列,而 SSIS 将 thous 检测为 CLOB。所以它不能说它不能将CLOB 转换为datetime2。

我已经在 SQL Server 数据库中创建了表。所以它只是通过一些类型转换来移动数据。

我正在使用 SQL Server Management Studio (SSMS) 中的 SQL Server 导入和导出向导 (SSIS)。

我使用 .NET Framework Data Provider for Oracle 连接到 Oracle DB,使用 SQL Server Native Client 11.0 连接到我的 SQL Server。

我的源类型是TIMESTAMP(0) WITH TIME ZONE,我的目标类型是datetime2。

这是我得到的错误:

[Source Information]
Source Location : localhost
Table: "MYSPACE"."MYTABLE"
Column: START_DATE
Column Type: CLOB
SSIS Type: Unicode text stream [DT_NTEXT]
Mapping file (to SSIS type): C:\Program Files (x86)\Microsoft SQL Server\140\DTS\MappingFiles\OracleClientToSSIS10.XML

[Destination Information]
Destination Location : localhost
Destination Provider : SQLNCLI11
Table: [dbo].[mytable]
Column: start_date
Column Type: datetime2
SSIS Type: database timestamp with precision [DT_DBTIMESTAMP2]
Mapping file (to SSIS type): C:\Program Files (x86)\Microsoft SQL Server\140\DTS\MappingFiles\MSSQLToSSIS10.XML

[Conversion Steps]
Conversion unknown ...
SSIS conversion file: C:\Program Files (x86)\Microsoft SQL Server\140\DTS\binn\DtwTypeConversion.xml

如您所见,START_DATE 列被检测为 CLOB。这是不正确的。

我查看了 OracleClientToSSIS10.XML 的内部

<!-- TIMESTAMP 10.* -->
<dtm:DataTypeMapping >
    <dtm:SourceDataType>
        <dtm:DataTypeName>timestamp</dtm:DataTypeName>
    </dtm:SourceDataType>
    <dtm:DestinationDataType>
        <dtm:NumericType>
            <dtm:DataTypeName>DT_DBTIMESTAMP2</dtm:DataTypeName>
            <dtm:SkipPrecision/>
            <dtm:UseSourceScale/>
        </dtm:NumericType>
    </dtm:DestinationDataType>
</dtm:DataTypeMapping>  

<!-- TIMESTAMP WITH TIME ZONE 10.* -->
<dtm:DataTypeMapping >
    <dtm:SourceDataType>
        <dtm:DataTypeName>TIMESTAMP WITH TIME ZONE</dtm:DataTypeName>
    </dtm:SourceDataType>
    <dtm:DestinationDataType>
        <dtm:NumericType>
            <dtm:DataTypeName>DT_DBTIMESTAMPOFFSET</dtm:DataTypeName>
            <dtm:SkipPrecision/>
            <dtm:UseSourceScale/>
        </dtm:NumericType>
    </dtm:DestinationDataType>
</dtm:DataTypeMapping>  

<!-- CLOB -->
<dtm:DataTypeMapping >
    <dtm:SourceDataType>
        <dtm:DataTypeName>CLOB</dtm:DataTypeName>
    </dtm:SourceDataType>
    <dtm:DestinationDataType>
        <dtm:CharacterStringType>
            <dtm:DataTypeName>DT_NTEXT</dtm:DataTypeName>
            <dtm:Length>255</dtm:Length>
        </dtm:CharacterStringType>
    </dtm:DestinationDataType>
</dtm:DataTypeMapping>

看起来不错吧?

【问题讨论】:

  • 不是 Oracle 人员,但他们的日期/时间表示与 SQL Server 之间的差异是/曾经是已知的不兼容。在没有看到数据实际外观的示例的情况下,我会假设源值中有实际的偏移值-05:00,它不会映射到 datetime2。我会做相当于将 Oracle 中的日期转换为格式为 yyyy-mm-ddThh:mm:ss.ms 的字符字段,并让 Oracle 也处理时区。否则,您需要将 SQL Server 中的目标类型更改为 datetimeoffset,然后在 SSIS 的字符串中包含偏移信息
  • 出于测试目的,我添加了一个带有 NULL 值的 TIMESTAMP(无时区)列,并尝试将其映射到 MSSQL 中的 datetime2 列。它仍被检测为CLOB 列。所以认为实际值在这个阶段是无关紧要的。
  • 框架随附的适用于 Oracle 的 .NET 框架提供程序已过时且不受支持 - 自首次包含以来,它基本上没有更新。使用 Oracle 自己的提供程序。如果所有其他方法都失败了,一个相当简单的解决方法是首先通过查询或视图将 Oracle 中的数据类型转换为任何有效的数据类型(如果您使用DATETIME2 作为目标而不是DATETIMEOFFSET 你大概不对于初学者来说,不需要时区信息,所以 Oracle 端的普通 TIMESTAMP 也应该这样做)。
  • 解决此问题的第一步是让 SSIS 不将您的传入列检测为 CLOB/DT_NTEXT。这就是为什么我指出您应该使用 Oracle 等效项将日期时间转换为 ISO 格式的字符串。然后,SSIS 引擎将检测到 DT_STR/DT_WSTR,然后将您的数据推送到目标的提供程序应该具有转换为目标格式的智能。
  • 它应该显示为Microsoft OLE DB Provider for Oracle。我还验证了我可以在导入/导出向导中使用它。

标签: sql-server oracle ssis ssms


【解决方案1】:

在处理 Oracle 格式的 date-ish 数据类型时,我也遇到过类似的问题。我发现有效的方法是将数据转换为 Oracle 端的字符串,然后再将其拉过。然后,您可以在将数据导入 SQL Server 后对其进行转换。

您需要修改导入/导出向导以使用查询来指定要从 Oracle 服务器获取的数据。作为源查询的一部分,您可以像这样进行转换:

SELECT
CAST("START_DATE" AS VARCHAR2(26)) AS "StartDate"
FROM MYSPACE.MYTABLE

然后在 SQL Server 中,您可以像这样转换为日期时间:

SELECT CAST(CONVERT(DATETIMEOFFSET, StartDate) AS DATETIME)

如果您使用的是 SQL Server 2016 或更高版本,则可以使用 AT TIME ZONE 和您的本地时区来获取调整后的日期时间值:

SELECT CAST(CONVERT(DATETIMEOFFSET, StartDate) AT TIME ZONE 'Pacific Standard Time' AS DATETIME)

【讨论】:

    【解决方案2】:

    我无法让任何驱动程序为日期列工作。

    所以我最终进入 SQL Developer 并将带有日期列的表导出到 INSERT 语句,然后对其运行正则表达式替换。

    欢乐时光:(

    【讨论】:

      【解决方案3】:

      这个答案已经过期了,但至少我可以为这个特定任务添加一个可行的解决方案。我必须做一个几乎和你一样的任务,唯一的区别是数据库版本和源数据类型(我有一个没有时区的TIMESTAMP)。我遇到了同样的驱动错误,未能将CLOB 转换为datetime2。

      前言和错误说明

      我注意到您的源数据类型TIMESTAMP(0) WITH TIME ZONE 存储时区,而您的目标数据类型datetime2 没有。它不会对转换步骤产生太大影响,但要记住一些事情。

      在我的故障排除中,我还看到了 OracleClientToSSIS10.XML 文件,并看到了您在帖子中列出的相同详细信息。起初我认为 timestamp 条目无效,因为它不是大写的,但我确认它确实有效。

      使用文件导入向导,导入器将从源读取,绑定到 SSIS 数据类型,转换为间歇性 SSIS 数据类型,然后转换为有效的目标数据类型。当您阅读错误消息时,您必须从 SSIS 的角度来看待它。

      OracleClientToSSIS10.XML 确实是文件导入器用来将源数据类型绑定到有效 SSIS 数据类型的东西。发生在您身上的是源数据类型与导入程序对该数据类型的期望不完全匹配,因此它使用了下一个最好的东西,CLOB,它映射到 DT_NTEXT。要查看进口商对DT_DBTIMESTAMPOFFSET 的期望,请转至here。

      但这不是导致您的错误的原因。如果您查看错误消息的底部,它会列出 DtwTypeConversion.xml 这是在 SSIS 数据类型之间转换的文件。

      C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\CommonExtensions\Microsoft\SSIS\150\Binn\DtwTypeConversion.xml

      (仅过滤为 DT_NTEXT)

        <!-- Convert from DT_TEXT-->
        <dtw:ConversionEntry>
          <dtw:SourceType>DT_TEXT</dtw:SourceType>
          <dtw:DestinationType TypeName="DT_STR">
            <dtw:ConversionStep StepNum="1" ConvertToType="DT_STR"/>
          </dtw:DestinationType>
          <dtw:DestinationType TypeName="DT_WSTR">
            <dtw:ConversionStep StepNum="1" ConvertToType="DT_STR"/>
            <dtw:ConversionStep StepNum="2" ConvertToType="DT_WSTR"/>
          </dtw:DestinationType>
          <dtw:DestinationType TypeName="DT_IMAGE">
            <dtw:ConversionStep StepNum="1" ConvertToType="DT_IMAGE"/>
          </dtw:DestinationType>
          <dtw:DestinationType TypeName="DT_NTEXT">
            <dtw:ConversionStep StepNum="1" ConvertToType="DT_NTEXT"/>
          </dtw:DestinationType>
        </dtw:ConversionEntry>
      
        
        <!-- Convert from DT_NTEXT-->
        <dtw:ConversionEntry>
          <dtw:SourceType>DT_NTEXT</dtw:SourceType>
          <dtw:DestinationType TypeName="DT_STR">
            <dtw:ConversionStep StepNum="1" ConvertToType="DT_WSTR"/>
            <dtw:ConversionStep StepNum="2" ConvertToType="DT_STR"/>
          </dtw:DestinationType>
          <dtw:DestinationType TypeName="DT_WSTR">
            <dtw:ConversionStep StepNum="1" ConvertToType="DT_WSTR"/>
          </dtw:DestinationType>
          <dtw:DestinationType TypeName="DT_IMAGE">
            <dtw:ConversionStep StepNum="1" ConvertToType="DT_IMAGE"/>
          </dtw:DestinationType>
          <dtw:DestinationType TypeName="DT_TEXT">
            <dtw:ConversionStep StepNum="1" ConvertToType="DT_TEXT"/>
          </dtw:DestinationType>
        </dtw:ConversionEntry>
      

      如果您查看&lt;!-- Convert from DT_NTEXT--&gt; 块,您会注意到DT_DBTIMESTAMP2 没有子树,这意味着没有从DT_NTEXT 到DT_DBTIMESTAMP2 的转换。这就是导致错误的原因(DT_DBTIMESTAMP2 是 datetime2 的 SSIS 等效项;您可以验证它here)。

      如何解决问题 您将需要:

      1. 让导入器识别DT_DBTIMESTAMPOFFSET。老实说,我认为您不会让它们匹配,其他人表示日期时间转换不兼容或棘手
      2. 更改您的源查询以将TIMESTAMP 预转换为字符串,然后让导入程序将其转换为datetime2。执行此操作时,您必须确保 DtwTypeConversion.xml 文件具有从您选择的字符串到 DT_DBTIMESTAMP2 的转换

      所列解决方案的系统版本

      源数据库:Oracle 12c (12.1.0.2.0)

      • 数据类型:主要是 varchar2、数字和一个带时间戳 (2) 的字段(无时区)

      目标数据库:Microsoft SQL Server 2019

      • 数据类型:较小的字段(varchar2 减少,一些变成了 char,一些数字变成了 int 或 smallint 或 tinyint)。时间戳字段变成了datetime2

      SSMS:18.9.1

      解决方案 #1:使用 SSMS - SQL Server 导入和导出向导 - Oracle 驱动程序

      1. 打开 DtwTypeConversion.xml,进入 &lt;!-- Convert from DT_NTEXT--&gt; 部分,添加子树,然后保存

           <!-- Convert from DT_WSTR-->
           <dtw:ConversionEntry>
             <dtw:SourceType>DT_WSTR</dtw:SourceType>
             <dtw:DestinationType TypeName="DT_I1">
               <dtw:ConversionStep StepNum="1" ConvertToType="DT_I1"/>
             </dtw:DestinationType>
        

        ...

             <dtw:DestinationType TypeName="DT_DBTIME">
               <dtw:ConversionStep StepNum="1" ConvertToType="DT_DBTIME"/>
             </dtw:DestinationType>
             <dtw:DestinationType TypeName="DT_DBTIMESTAMP">
               <dtw:ConversionStep StepNum="1" ConvertToType="DT_DBTIMESTAMP"/>
             </dtw:DestinationType>
             <dtw:DestinationType TypeName="DT_DBTIMESTAMP2">
               <dtw:ConversionStep StepNum="1" ConvertToType="DT_DBTIMESTAMP2"/>
             </dtw:DestinationType>
             <dtw:DestinationType TypeName="DT_FILETIME">
               <dtw:ConversionStep StepNum="1" ConvertToType="DT_FILETIME"/>
             </dtw:DestinationType>
        
      2. 源驱动:

      • .NET Framework Data Provider for Oracle - 最终使用了这个,因为它很有效,很容易构建连接字符串,并且需要最少的工作量来进行数据类型转换。
      • Microsoft OLE DB Provider for Oracle - 这个可以工作,但登录过程并不总是有效,而且它一直给我的文本字段是 Unicode/非 Unicode 的问题
      • Oracle Provider for OLE DB - 这个一直给我数字转换错误,所以要多花点力气使用
      1. 目标驱动程序:
      • SQL Server Native Client 11.0
      1. 创建一个源查询,将时间戳转换为字符,类似于

         select
             allOtherFields
             ,TO_CHAR(TIMESTAMP '2021-08-02 14:30:20.05 -05:00','YYYY-MM-DD HH24:MI:SS.FF7') as converted_str
         from mySchema.myTable
        
      • 要仅测试一个子集,您可以在表名后添加 FETCH FIRST 100 ROWS ONLY
      1. 查看映射时,您应该会看到时间戳字段通过检查且没有任何错误
      2. 运行和导入

      解决方案 #2:使用 SSMS - SQL Server 导入和导出向导,平面文件源

      1. 创建一个将时间戳转换为字符的查询,类似于

         select
         allOtherFields
         --,TIMESTAMP '2021-08-02 14:30:20.05 -05:00' as original_timestamp_with_zone
         ,TO_CHAR(TIMESTAMP '2021-08-02 14:30:20.05 -05:00','YYYY-MM-DD HH24:MI:SS.FF7') as converted_str
         from mySchema.MyTable;
        
      • 要仅测试一个子集,您可以在表名后添加 FETCH FIRST 100 ROWS ONLY
      • 确保字符串输出与您要访问的 datetime2 字段完全匹配。上面的例子应该足够了
      1. 将查询输出保存到 csv 文件,最好使用文本限定符
      2. 源驱动程序:
      • Flat File Source
        • 确保设置了文本限定符
        • 确保将时间列设置为字符串或 DT_WSTR
      1. 目标驱动程序:
      • SQL Server Native Client 11.0
      1. 查看映射时,您应该会看到时间戳字段通过检查且没有任何错误
      2. 运行和导入

      替代方案:使用 SSMA 工具

      Microsoft docs 将是一个很好的指南。 起初它看起来很简单,但我最终不得不做很多代码来准备表格,因为生成的代码 sn-ps 无法正确获取索引并且数据文件将始终默认为 [PRIMARY] 即使在完成所有编码之后,我最终还是放弃了这种方法,因为我遇到了太多行错误,并且除了“表已部分处理”之外没有得到太多信息。如果您只是迁移较小的表而没有太多的数据类型转换,那么您会没事的

      【讨论】:

        猜你喜欢
        • 2010-10-08
        • 1970-01-01
        • 2018-04-07
        • 2015-04-17
        • 2010-10-14
        • 2018-11-26
        • 1970-01-01
        • 2014-02-09
        • 2011-06-09
        相关资源
        最近更新 更多