如何将多个时间单位的字符串转换为Oracle SQL中的数字

时间:2017-08-25 14:21:14

标签: sql oracle

我在oracle 10数据库中有一个Varchar字段,人们可以手动输入工作时间。我需要开发模糊逻辑,将信息转换为小时数,以便在函数中求和。

9 HOURS
6.0 Hrs
4.5 hours
14 HOURS 30 MIN
17 HOURS 15 MINUTES
10.75 Days

我正在使用以下代码来创建一个等式,但我无法将其转换为数字。有人曾经这样做过吗?

select 
replace(
    replace (
        replace(
         translate(upper('3 Day 3.2 Hours 6 Min '),'DHMAFBCEFGIJKLNOPQRSTUVWXYZ','DHM')
                  , 'H', '*1+'),
              'M', '/60'),
           'D','*24+')       
from dual

2 个答案:

答案 0 :(得分:2)

这是执行此操作的“脏”方法(不更改当前设置中的任何内容)。我同意其他人认为这是一个非常糟糕的模型,最好改变它。

解决方案的复杂程度取决于您愿意在输入中允许的灵活性(自由文本)。我假设数字可以是任何格式3 3.5 3.0 12. 0.3 .33(但没有符号,+或 - );一个数字后面可以跟零个或多个空格;任何时间成分(限于天,小时和分钟)可能存在或不存在;并且组件由跟随它们的单词的第一个字母“标记”:D,H或M.(因此,这也是语言相关的,如果您允许其他语言中的自由文本,则系统的另一个弱点。)D, H或M可以是大写或小写,可以跟随任何其他字母(DAY,Days,dy,dd等)

with
     inputs ( str ) as (
       select '9 HOURS'              from dual union all
       select '6.0 Hrs'              from dual union all
       select '4.5 hours'            from dual union all
       select '14 HOURS 30 MIN'      from dual union all
       select '17 HOURS 15 MINUTES'  from dual union all
       select '10.75 Days'           from dual union all
       select '.5days 30m'           from dual union all
       select '30. h'                from dual union all
       select '2 days 1 hour 30 min' from dual
     )
-- End of simulated inputs (for testing only, not part of the solution).
-- Use your actual table and column names in the SQL query below.
select str,
       nvl(to_number(regexp_substr(str, '(\d+(\.\d*)?|\.\d+)\s*D', 1, 1, 'i', 1)), 0)*24 +
       nvl(to_number(regexp_substr(str, '(\d+(\.\d*)?|\.\d+)\s*H', 1, 1, 'i', 1)), 0)    +
       nvl(to_number(regexp_substr(str, '(\d+(\.\d*)?|\.\d+)\s*M', 1, 1, 'i', 1)), 0)/60
       as hours_worked
from   inputs;

STR                  HOURS_WORKED
-------------------- ------------
9 HOURS                      9.00
6.0 Hrs                      6.00
4.5 hours                    4.50
14 HOURS 30 MIN             14.50
17 HOURS 15 MINUTES         17.25
10.75 Days                 258.00
.5days 30m                  12.50
30. h                       30.00
2 days 1 hour 30 min        49.50

答案 1 :(得分:0)

我的建议是添加新列并逐步更新。

ALTER TABLE WORKING_TIME_TABLE ADD (WORKING_INTERVAL INTERVAL DAY(3) TO SECOND(0));

UPDATE WORKING_TIME_TABLE 
SET WORKING_INTERVAL = REGEXP_REPLACE(WORKING_TIME, 'HOURS$', NULL, 1,1,'i') * INTERVAL '1' HOUR
WHERE WORKING_INTERVAL IS NULL
   AND (REGEXP_LIKE(WORKING_TIME, '^\d+ HOURS$', 'i') 
     OR REGEXP_LIKE(WORKING_TIME, '^\d+\.\d+ HOURS$', 'i')) ;  

UPDATE WORKING_TIME_TABLE 
SET WORKING_INTERVAL = REGEXP_REPLACE(WORKING_TIME, 'Hrs$', NULL, 1,1,'i') * INTERVAL '1' HOUR
WHERE WORKING_INTERVAL IS NULL
   AND (REGEXP_LIKE(WORKING_TIME, '^\d+ Hrs$', 'i')
     OR REGEXP_LIKE(WORKING_TIME, '^\d+\.\d+ Hrs$', 'i') );

UPDATE WORKING_TIME_TABLE 
SET WORKING_INTERVAL = REGEXP_SUBSTR(WORKING_TIME, '^\d+') * INTERVAL '1' HOUR + REGEXP_SUBSTR(WORKING_TIME, '\d+', 1,2) * INTERVAL '1' MINUTE
WHERE WORKING_INTERVAL IS NULL
   AND REGEXP_LIKE(WORKING_TIME, '^\d+ HOURS \d+ MIN(UTES)?$', 'i');


UPDATE WORKING_TIME_TABLE 
SET WORKING_INTERVAL = REGEXP_SUBSTR(WORKING_TIME, '^\d+\.\d+') * INTERVAL '1' DAY
WHERE WORKING_INTERVAL IS NULL
   AND REGEXP_LIKE(WORKING_TIME, '^\d+\.\d+ Days$', 'i');


etc.

当然,在您成功更新所有WORKING_INT后,请更改您的应用程序以强制执行适当的间隔值!