在 Teradata 中安全地将 VARCHAR 转换为 DECIMAL

Posted

技术标签:

【中文标题】在 Teradata 中安全地将 VARCHAR 转换为 DECIMAL【英文标题】:Safe casting VARCHAR to DECIMAL in Teradata 【发布时间】:2012-04-18 12:00:15 【问题描述】:

在 Teradata DB 中我有源表

create set table SRC_TABLE (
    Some_Id varchar(2O) not null
);

此表加载了来自外部系统的数据。我有目标表

create set table DST_TABLE (
    Some_Id decimal(4,0) not null
);

我需要安全地将行从 SRC_TABLE 复制到 DST_TABLE。有一个合同,外部系统将只提供可转换为 DECIMAL(4) 的值。但是,有没有什么安全的方法可以选择 SRC_TABLE 中不符合约定并可能导致类型转换失败的行?

更新:由于工作环境的限制,我无法使用 UDF 函数。

【问题讨论】:

使用 trycast() ***.com/questions/39386736/… 【参考方案1】:

您可以使用 FastExport 从旧表中写出数据,然后使用 FastLoad 将其加载到新表中。任何解析为 decimal(4, 0) 的记录都将被加载到新表中,而其余记录将被写入错误表。确保设置足够高的ERRLIMIT,以确保作业不会在出现少量错误后终止。

【讨论】:

感谢您的提示。不幸的是,我在 DWH 加载过程的某些部分执行此操作,我无法使用 FastExport/FastLoad,并且仅限于普通 Teradata SQL。【参考方案2】:

我建议在带有错误表的 SQL 中使用MERGE INTO 操作来捕获无法应用的记录。这将允许您加载数据并对无法应用的错误表中的记录进行后处理。

您还可以从 Teradata Developer Exchange 下载适当的 UDF 库,并使用 IsNumeric() 等效项对 SRC_TABLE 的每一行执行条件检查,以避免将非数字数据插入表中。这种条件检查可以丢弃整条记录,将记录加载到日志表中,或者将值设置为商定的无效数据默认值。

CREATE ERROR TABLE MyDB.TGT_TABLE_ERR FOR MyDB.TGT_TABLE; -- Creates Error Table for MERGE INTO operation

MERGE INTO MyDB.TGT_TABLE T1
     USING MyDB.SRC_TABLE T2
        ON T1.primary index = T2.primary index
WHEN MATCHED THEN
     UPDATE SET Some_ID = CAST(T2.Some_ID AS DECIMAL(4,0))
WHEN NOT MATCHED THEN
     INSERT VALUES (T2.column list)
LOGGING ALL ERRORS WITH NO LIMIT;

【讨论】:

Rob,您能否更具体地了解 MERGE INTO 操作?我搜索了互联网和 Teradata 文档,但一无所获。不幸的是,由于环境先决条件,我无法使用 UDF 库。 我正在开发 13.10。请问你能用 MERGE INTO 语句的例子更新你的答案吗? 很遗憾,ERROR TABLEs 不支持转换错误记录。【参考方案3】:

最后在同事的帮助下,我找到了可行的解决方案。它有一些限制(不考虑符号,不考虑小数部分),但对于 ID,它工作得很好。

    从字符串的开头和结尾修剪空格 从字符串中去除前导零 测试最大允许长度 用零填充字符串到四个字符(在字符串开头添加四个零并从字符串中获取最后四个字符) 在允许的字符集上测试字符串中的每个位置

所以SRC_TABLE中不能转换为DECIMAL(4)的记录可以通过select获取:

select 
  Some_Id
from
  SRC_TABLE
where
  characters(trim(leading '0' from trim(both ' ' from Some_Id))) > 4
  or substring(substring('0000' || trim(leading '0' from trim(both ' ' from Some_Id)) FROM characters('0000' || trim(leading '0' from trim(both ' ' from Some_Id))) - 3) FROM 1 FOR 1) NOT IN ('0','1','2','3','4','5','6','7','8','9')  
  or substring(substring('0000' || trim(leading '0' from trim(both ' ' from Some_Id)) FROM characters('0000' || trim(leading '0' from trim(both ' ' from Some_Id))) - 3) FROM 2 FOR 1) NOT IN ('0','1','2','3','4','5','6','7','8','9')
  or substring(substring('0000' || trim(leading '0' from trim(both ' ' from Some_Id)) FROM characters('0000' || trim(leading '0' from trim(both ' ' from Some_Id))) - 3) FROM 3 FOR 1) NOT IN ('0','1','2','3','4','5','6','7','8','9')
  or substring(substring('0000' || trim(leading '0' from trim(both ' ' from Some_Id)) FROM characters('0000' || trim(leading '0' from trim(both ' ' from Some_Id))) - 3) FROM 4 FOR 1) NOT IN ('0','1','2','3','4','5','6','7','8','9');

编辑: dnoeth 在他对 Convert char to int TeraData Sql 的回答中建议的方式更方便,它也适用于 TD 13.10:

-- TO_NUMBER returns NULL when failing

CAST(TO_NUMBER(UTENTE_CD) AS INTEGER)

-- check if there are only digits
CASE WHEN UTENTE_CD  = ''                     -- all spaces
       THEN NULL
     WHEN LTRIM(UTENTE_CD, '0123456789') = '' -- only digits
       THEN CAST(UTENTE_CD AS INTEGER)
     ELSE NULL
END

【讨论】:

很好地提出了解决方案。请注意,虽然它适用于您的特定情况,但它并不等同于正确的数字解析器,因为使用此方法将认为负数和带有小数点的数字无效。

以上是关于在 Teradata 中安全地将 VARCHAR 转换为 DECIMAL的主要内容,如果未能解决你的问题,请参考以下文章

如何过滤掉 teradata 文本字段中的非数字值?

将 GPS 位置存储在数据库 varchar 字段中

在 Teradata 中修剪字符串

teradata建表DDL

在Java中安全地将long转换为int

安全地将 JSON 字符串转换为对象