在Teradata中将任意类日期字符串转换为日期

时间:2016-03-25 13:39:41

标签: sql function teradata

我有一个包含此类字符串值的列

12/01/1999
13021999
140301
bla bla 140302
just bla bla

它们具有相对定义格式的日期,具有日期或任意文本的任意文本。

我需要将此数据放在date数据类型的列中。我如何在Teradata中做到这一点?

如果在Oracle中,我会编写一些程序来执行一些regexp / replace,然后将结果转换为日期。当异常返回null时。然后我会插入/选择并使用此过程,这是我的日期列。

在Teradata 14.10中,您可以编写SQL用户定义的函数。但它只有声明 - 返回表达式。无法像处理程序那样进行异常处理和其他语句。

或者您可以在C / Java中编写外部函数。然后你必须安装它。我不确定我的环境是否拥有这样的权利。另外,我不是C / Java。

此任务的其他选项是什么?

1 个答案:

答案 0 :(得分:2)

在Teradata有很多方法可以解决这个问题。

您可以在存储过程中获得幻想,将光标移动到记录集并在那里执行逻辑。它会很慢,但逻辑很干净。

我只是在SQL语句中这样做,并且对CASE语句逻辑感到满意。我会使用strtok_split_to_table将每条记录分成单词,然后检查使用正则表达式返回的每个标记,以及如何弄清楚它是如何格式化的,并尝试根据它进行转换。类似的东西:

CREATE MULTISET VOLATILE TABLE uglydates
(
    id INTEGER,
    uglydate VARCHAR(100)

)UNIQUE PRIMARY INDEX ("id") ON COMMIT PRESERVE ROWS;


INSERT INTO uglydates VALUES (1, '12/01/1999');
INSERT INTO uglydates VALUES (2, '13021999');
INSERT INTO uglydates VALUES (3, '140301');
INSERT INTO uglydates VALUES (4, 'bla bla 140302');
INSERT INTO uglydates VALUES (5, 'just bla bla');

SELECT
    outkey,
    token,
    CASE 
        WHEN REGEXP_SIMILAR(token, '^[0-9,/,-]*$') = 1 
            THEN /* We've got a date... maybe */            
                CASE 
                    WHEN "LENGTH"(token) = 10 AND length(OREPLACE(token, '/', '')) = "LENGTH"(token)-2 
                        THEN /* if the formats always the same in this scenario then */
                            CAST(token AS DATE FORMAT 'mm/dd/yyyy')
                    WHEN "LENGTH"(token) = 6 
                        THEN
                            CAST(token AS DATE FORMAT 'yymmdd')
                    END
            ELSE NULL
    END
FROM
    (
        SELECT d.token, d.outkey
        FROM TABLE (strtok_split_to_table(uglydates.id, uglydates.uglydate, ' ')
                RETURNS (outkey integer, tokennum integer, token varchar(20)character set unicode) ) as d
    ) tokens

这是一个相当简单的骨头启动器,但它可以让你朝着正确的方向前进。我使用strtok_split_to_table来解析丑陋的日期文本并返回每个单词的记录,以及记录的ID,以便我可以稍后插入/更新而不会丢失记录的引用。剩下的就是非常简单的正则表达式,以及对于你在Oracle中所做的事情看起来应该是什么样的。

上面的示例生成以下输出:

+--------+------------+--------------------+
| outkey |   token    | <CASE  expression> |
+--------+------------+--------------------+
|      5 | just       |                    |
|      3 | 140301     | 2014-03-01         |
|      4 | bla        |                    |
|      1 | 12/01/1999 | 1999-12-01         |
|      2 | 13021999   |                    |
|      5 | bla        |                    |
|      4 | bla        |                    |
|      5 | bla        |                    |
|      4 | 140302     | 2014-03-02         |
+--------+------------+--------------------+

要将其与您关心的日期配对,您可以使用Teradata的QUALIFY子句,该子句允许您使用窗口函数来过滤结果集。它类似于在SELECT子句中执行窗口函数,将结果粘贴在子查询中,并在Window Function结果字段上使用WHERE语句,但没有所有额外的代码。类似的东西:

SELECT
    outkey,
    token,
    CASE 
        WHEN REGEXP_SIMILAR(token, '^[0-9,/,-]*$') = 1 
            THEN /* We've got a date... maybe */            
                CASE 
                    WHEN "LENGTH"(token) = 10 AND length(OREPLACE(token, '/', '')) = "LENGTH"(token)-2 
                        THEN /* if the formats always the same in this scenario then */
                            CAST(token AS DATE FORMAT 'mm/dd/yyyy')
                    WHEN "LENGTH"(token) = 6 
                        THEN
                            CAST(token AS DATE FORMAT 'yymmdd')
                    WHEN "LENGTH"(token) = 8
                        THEN
                            CAST(token AS DATE FORMAT 'ddmmyyyy')
                    ELSE NULL
                    END
            ELSE NULL
    END AS outdate
FROM
    (
        SELECT d.token, d.outkey
        FROM TABLE (strtok_split_to_table(uglydates.id, uglydates.uglydate, ' ')
                RETURNS (outkey integer, tokennum integer, token varchar(20)character set unicode) ) as d
    ) tokens
QUALIFY ROW_NUMBER() OVER (PARTITION BY outkey ORDER BY outdate DESC) = 1

将按outkey对数据进行分区,按照我们按降序排列的日期对其进行排序,然后为每个outkey选择该排序中的第一条记录。结果集如下所示:

+--------+------------+------------+
| outkey |   token    |  outdate   |
+--------+------------+------------+
|      1 | 12/01/1999 | 1999-12-01 |
|      2 | 13021999   | 1999-02-13 |
|      3 | 140301     | 2014-03-01 |
|      4 | 140302     | 2014-03-02 |
|      5 | bla        |            |
+--------+------------+------------+

有了这个,你就准备好了UPDATE语句。

在SQL语句中完成所有操作的一件很酷的事情是,您允许Teradata为您优化执行路径。即使在大型桌子上,这也会很快工作,其中带有光标的SP(虽然更容易读取逻辑)会非常慢。自定义功能将仅限于单放大器处理(我相信即使在15.10中仍然如此,对吧?我肯定不知道100%),所以即使你有一个正确索引的表具有良好的倾斜度,您将在盒子的一小部分上操作最耗费精力的逻辑。 @dnoeth,请保持诚实。