在 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的主要内容,如果未能解决你的问题,请参考以下文章