我有一个包含此类字符串值的列
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。
此任务的其他选项是什么?
答案 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,请保持诚实。