在 Oracle 中将 Varchar2 转换为时间 Sql

Converting Varchar2 to Time in Oracle Sql

我有一个数据加载到 table 中,我有两列时间输入和时间输出,它们都是 Varchar2 类型。加载到这些列中的数据是带时间的日期,我想排除日期并仅用时间填充列。我的问题是我尝试将列中的所有日期格式化为格式,它给我错误“不是有效月份”或“无效数字”。我有一个查询,用于尝试 select 格式的数据。如果我能得到正确的查询,那么我就可以修复我的数据。

查询包括

SELECT
    to_date(to_char(time_in, 'HH:MI:SS AM'), 'HH:MI:SS AM') time_in,
    to_date(to_char(time_out, 'HH:MI:SS AM'), 'HH:MI:SS AM') time_out
FROM
    loadingtable

回顾一下,我有一个 table,其中包含两个类型为 varchar2 的列,并且它们具有日期和时间两种格式的数据。

而且我想格式化它以腾出时间。

提前致谢!

你可以这样做:

WITH 
function convert_date(str_i VARCHAR2) RETURN DATE
AS
  l_date DATE;
  TYPE format_mask_t IS TABLE OF VARCHAR2(128);
   l_format_masks format_mask_t := format_mask_t(
     'DD/MM/YY HH:MI AM'
    ,'DD-MM-YYYY HH:MI AM' -- add more if needed
   );
BEGIN
  FOR i IN l_format_masks.FIRST .. l_format_masks.LAST LOOP
    BEGIN
      l_date := TO_DATE(str_i, l_format_masks(i));
      RETURN l_date;
    EXCEPTION WHEN OTHERS THEN
      NULL;
    END;  
  END LOOP;
  RETURN NULL;
END;
source_tab (time_in, time_out) AS
(
  SELECT '12/2/21 7:03 AM', '12/2/21 4:24 PM' FROM DUAL UNION ALL
  SELECT '12-02-2021 7:01 AM', '15-02-2021 4:00 PM' FROM DUAL 
)
SELECT TO_CHAR(convert_date(time_in),'HH:MI AM') as time_in,
       TO_CHAR(convert_date(time_out),'HH:MI AM') as time_out
 FROM source_tab

这是使用内联函数 - 您可以创建一个函数而不是使用该内联函数。如果遇到与列出的 2 种格式中的任何一种都不匹配的格式,它 returns NULL。如果需要,您可以轻松添加其他格式。 “source_tab”CTE 只是为了生成一些测试数据,将其从您自己的数据的查询中删除。

您可以直接在格式模型中使用 case 表达式。如果您确定只有这两个确切的模型,则可以区分两种格式:字符串包含正斜杠,或者不包含 - 并在每种情况下使用相应的格式模型。这可以进一步推广到更多的“混合”格式模型——只要没有歧义。

像这样:

with
  source_tab (time_in, time_out) as (
    select '12/2/21 7:03 AM'   , '12/2/21 4:24 PM'    from dual union all
    select '12-02-2021 7:01 AM', '15-02-2021 4:00 PM' from dual 
  )
select to_date(time_in , case when time_in like '%/%' then 'dd/mm/rr hh:mi AM'
                              else 'dd-mm-yyyy hh:mi AM' end) as time_in,
       to_date(time_out, case when time_in like '%/%' then 'dd/mm/rr hh:mi AM'
                              else 'dd-mm-yyyy hh:mi AM' end) as time_out
from   source_tab
;

TIME_IN             TIME_OUT           
------------------- -------------------
2021-02-12 07:03:00 2021-02-12 16:24:00
2021-02-12 07:01:00 2021-02-15 16:00:00

(当然,上面显示的输出取决于我的显示日期时间的会话设置。)

如果您有很多这样的表,或者如果您必须针对这些数据编写许多类似的查询,您可以编写一个 PL/SQL 函数将“string”转换为“date”(其中“string”可能使用各种格式模型),但它应该遵循相同的总体思路。在“正确答案”中尝试不同模型直到不抛出异常的建议似乎不是一个好的做法。

有了这两种日期格式,您不需要使用 CASE 语句或多种格式,只需使用 string-to-date conversion rules where:

  • 您可以在日期字符串中使用任何非字母数字字符来匹配格式字符串中的标点符号。
  • 'RR' 也匹配 'RRRR'.

这给你:

SELECT TO_CHAR( TO_DATE( time_in, 'DD-MM-RR HH12:MI AM' ), 'HH24:MI' ) AS time_in,
       TO_CHAR( TO_DATE( time_out, 'DD-MM-RR HH12:MI AM' ), 'HH24:MI' ) AS time_out
FROM   table_name;

其中,对于示例数据:

CREATE TABLE table_name ( time_in, time_out ) AS
SELECT '12/2/21 7:03 AM', '12/2/21 4:24 PM' FROM DUAL UNION ALL
SELECT '15-02-2021 7:01 AM', '15-02-2021 04:00 PM' FROM DUAL

输出:

TIME_IN | TIME_OUT
:------ | :-------
07:03   | 16:24   
07:01   | 16:00   

db<>fiddle here

I want to exclude the dates and populate the column with the time alone

问题是:为此使用什么数据类型?让我们一步步来看...

首先,将日期时间存储为字符串是个坏主意。在您的情况下,您必须考虑至少两种不同的格式。您能保证所有字符串都是两种格式之一的有效日期时间吗?

第一步应该是将字符串转换为真正的日期时间。使用带有 ON CONVERSION ERROR 子句的 COALESCETO_DATE 来获取日期时间。与这两种格式都不匹配的字符串会导致 null:

with mytable as (select '12/2/21 7:03 am' as time_in, '15-02-2021 04:00 pm' as time_out from dual)
select 
  coalesce
  (
    to_date(time_in default null on conversion error, 'dd.mm.rr hh:mi am'), 
    to_date(time_in default null on conversion error, 'dd-mm-yyyy hh:mi am')
  ) as start_time,
  coalesce
  (
    to_date(time_out default null on conversion error, 'dd.mm.rr hh:mi am'), 
    to_date(time_out default null on conversion error, 'dd-mm-yyyy hh:mi am')
  ) as end_time
from mytable;

然后,Oracle 没有 TIME 数据类型(就像他们也没有单独的 DATE 数据类型一样),他们只有一个日期时间类型,他们称之为 DATE 不恰当。所以,你不能真正摆脱使用这种类型的日期部分。

三个选项:

  1. 改为存储字符串(通过对上述表达式应用 to_char(___, 'hh24:mi'))。
  2. 使用 INTERVAL 来存储时间(例如,通过对上述表达式应用 cast(___ as timestamp)- trunc(___)。我们需要从 DATETIMESTRING 的转换才能获得一个 INTERVAL 而不是一天的一小部分作为减法的数字。)
  3. 将日期部分替换为常量,例如 1990-01-01(将其添加到我们刚刚得到的时间间隔,例如 date '1990-01-01' + (cast(___ as timestamp)- trunc(___)))。

下面的演示显示了第二个选项,这是我的偏好:https://dbfiddle.uk/?rdbms=oracle_18&fiddle=189496f4b97e87e57ed51a20f3696134