【问题标题】:Difference between two time stored as text存储为文本的两次时间之间的差异
【发布时间】:2016-08-24 09:02:46
【问题描述】:

我有一张表格,上面有退房和入住时间,但这些时间是文本格式的。我想要这两次之间的差异。

例子:

Check out Check in
1015      1331
0642      0911
1002      1242
1434      1658
1821      2058
2221      0051

运行此查询时出现错误:

ORA-02000:缺少 ) 关键字
02000. 00000 - “缺少 %s 关键字”

【问题讨论】:

  • 您的解决方案有多远?你想要什么输出?您是否知道时间总是在同一天,或者至少少于 24 小时(最后一个示例超过了午夜)?
  • 如果您看到第 3 行 1002 1242 =(输出)2:44 .. 时间并不总是在同一天,它会在午夜 .. 例如 Checkin 24/08/2016 22:21:00 - - 退房 25/08/2016 00:51:00
  • 那么第三行应该是 2:40,而不是 2:44?您是否真的记录了这些日期(在另一列中;如果是,它也是文本还是正确的日期?)还是只是一个例子?
  • 是应用程序将这些日期记录在另一列中作为 Varchar2
  • 嗯,这是一个糟糕的数据模型 - 为什么不使用包含时间的正确数据列呢?但无论如何,您可以在转换时间时包含日期字符串,而不是默认为每月的第一天;然后你不要假设它只能跨越一天,但如果可以,那么你需要决定是显示天数还是显示小时值大于 24。

标签: sql oracle


【解决方案1】:

您可以使用to_date() 将字符串转换为名义日期的时间;如果您不提供日期部分,则默认为当月的第一天;使用 CTE(子查询分解)使用您的示例数据创建一个虚拟表 t

with t (check_out, check_in) as (
  select '1015', '1331' from dual
  union all select '0642', '0911' from dual
  union all select '1002', '1242' from dual
  union all select '1434', '1658' from dual
  union all select '1821', '2058' from dual
  union all select '2221', '0051' from dual
)
select check_out, check_in,
  to_char(to_date(check_out, 'HH24MI'), 'YYYY-MM-DD HH24:MI:SS') as d1,
  to_char(to_date(check_in, 'HH24MI'), 'YYYY-MM-DD HH24:MI:SS') as d2
from t;

CHEC CHEC D1                  D2                
---- ---- ------------------- -------------------
1015 1331 2016-08-01 10:15:00 2016-08-01 13:31:00
0642 0911 2016-08-01 06:42:00 2016-08-01 09:11:00
1002 1242 2016-08-01 10:02:00 2016-08-01 12:42:00
1434 1658 2016-08-01 14:34:00 2016-08-01 16:58:00
1821 2058 2016-08-01 18:21:00 2016-08-01 20:58:00
2221 0051 2016-08-01 22:21:00 2016-08-01 00:51:00

然后,您可以从另一个日期中减去其中一个日期。但是,当您的时间超过午夜并因此跨越两天时,如果签入时间表示的时间早于签出时间,您需要将签入时间调整一天 - 这是您上一个示例中的情况。您可以使用 case 表达式来解决这个问题,甚至可以将它们作为字符串进行比较,如果它们总是四个字符(零填充)。所以你可以这样做:

select check_out, check_in,
  to_char(to_date(check_out, 'HH24MI'), 'YYYY-MM-DD HH24:MI:SS') as d1,
  to_char(to_date(check_in, 'HH24MI') + case when check_in < check_out then 1 else 0 end,
    'YYYY-MM-DD HH24:MI:SS') as d2
from t;

CHEC CHEC D1                  D2                
---- ---- ------------------- -------------------
1015 1331 2016-08-01 10:15:00 2016-08-01 13:31:00
0642 0911 2016-08-01 06:42:00 2016-08-01 09:11:00
1002 1242 2016-08-01 10:02:00 2016-08-01 12:42:00
1434 1658 2016-08-01 14:34:00 2016-08-01 16:58:00
1821 2058 2016-08-01 18:21:00 2016-08-01 20:58:00
2221 0051 2016-08-01 22:21:00 2016-08-02 00:51:00

请注意,最后一行的签到时间现在是第二天。

然后减去,这会给你一天的一小部分作为差异:

select check_out, check_in,
  to_date(check_in, 'HH24MI')
    + case when check_in < check_out then 1 else 0 end -- adjust if check_in is next day
    - to_date(check_out, 'HH24MI') as diff
from t;

CHEC CHEC       DIFF
---- ---- ----------
1015 1331    0.13611
0642 0911    0.10347
1002 1242    0.11111
1434 1658    0.10000
1821 2058    0.10903
2221 0051    0.10417

如果您将该分数添加到另一个名义日期,其时间为午夜,那么您会得到一个日期,其时间部分与退房和入住时间之间的差异相匹配。然后,您可以随意格式化:

with t (check_out, check_in) as (
  select '1015', '1331' from dual
  union all select '0642', '0911' from dual
  union all select '1002', '1242' from dual
  union all select '1434', '1658' from dual
  union all select '1821', '2058' from dual
  union all select '2221', '0051' from dual
)
select check_out, check_in, to_char(trunc(sysdate) + diff, 'HH24:MI') as diff
from (
  select check_out, check_in,
    to_date(check_in, 'HH24MI')
      + case when check_in < check_out then 1 else 0 end -- adjust if check_in is next day
      - to_date(check_out, 'HH24MI') as diff
  from t
);

CHEC CHEC DIFF
---- ---- -----
1015 1331 03:16
0642 0911 02:29
1002 1242 02:40
1434 1658 02:24
1821 2058 02:37
2221 0051 02:30

您的真实查询将没有 CTE,但会直接从您的真实表中选择:

select OUTUDT, INUDT, to_char(trunc(sysdate) + diff, 'HH24:MI') as diff
from (
  select OUTUDT, INUDT,
    to_date(OUTUDT, 'HH24MI') + case when INUDT < OUTUDT then 1 else 0 end
    - to_date(INUDT, 'HH24MI') as diff 
  from your_real_table
)

当然,这是假设退房和入住之间的时间不超过 24 小时。由于您没有正确记录日期,因此您无法知道是否有任何跨度实际上比这更长。


如果您想使用“日期”列(也存储为文本),则可以将其与时间连接并转换为实际日期:

with t (check_out_date, check_out_time, check_in_date, check_in_time) as (
  select '24/08/2016', '1015', '24/08/2016', '1331' from dual
  union all select '23/08/2016', '0642', '23/08/2016', '0911' from dual
  union all select '23/08/2016', '1002', '23/08/2016', '1242' from dual
  union all select '23/08/2016', '1434', '23/08/2016', '1658' from dual
  union all select '24/08/2016', '1821', '24/08/2016', '2058' from dual
  union all select '24/08/2016', '2221', '25/08/2016', '0051' from dual
)
select check_out_date, check_out_time, check_in_date, check_in_time,
  to_char(trunc(sysdate) + diff, 'HH24:MI') as diff
from (
  select check_out_date, check_out_time, check_in_date, check_in_time,
    to_date(check_in_date || check_in_time, 'DD/MM/YYYYHH24MI')
      - to_date(check_out_date || check_out_time, 'DD/MM/YYYYHH24MI') as diff
  from t
);

CHECK_OUT_ CHEC CHECK_IN_D CHEC DIFF
---------- ---- ---------- ---- -----
24/08/2016 1015 24/08/2016 1331 03:16
23/08/2016 0642 23/08/2016 0911 02:29
23/08/2016 1002 23/08/2016 1242 02:40
23/08/2016 1434 23/08/2016 1658 02:24
24/08/2016 1821 24/08/2016 2058 02:37
24/08/2016 2221 25/08/2016 0051 02:30

如果您正确存储日期,则不需要进行连接和转换 - 当有特定数据类型可用时,将内容存储为字符串不是一个好主意。

如果时间段可能超过一天,您需要更改显示差异的方式。

【讨论】:

  • 我得到错误..当我将此查询与 t (OUTUDT,INUDT) as (select OUTUDT,INUDT from dual ) select OUTUDT,INUDT, to_char(trunc(sysdate) + diff, 'HH24 :MI') as diff from (select OUTUDT,INUDT, to_date(OUTUDT, 'HH24MI') + case when INUDT
  • t CTE 正在创建虚拟数据。改为查询您的真实表 - select outudt, ... as diff from your_real_table。 INUDT 和 OUTUDT 是只保存时间的列,而不是任何日期部分,对吧?
  • 我有日期列,我们可以用日期列和这个时间列减去
  • @FaisalKhan - 通过将日期和时间字符串转换为实际日期;我添加了一个例子。不过,您确实应该重新审视您的数据模型,看看是否可以将其更改为使用实际日期列。如果时间跨度超过一天,添加到 sysdate 的技巧将不起作用,但您需要决定要做什么,看看是否会发生这种情况。
  • Alex Poole 我使用了您的最后一个查询并且它有效,但是我使用此查询添加了更多表并且我收到错误(ORA-02000:缺少)关键字 02000。00000 -“缺少 %s 关键字”)我在主要问题中更新了我的查询
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-14
  • 2015-02-22
  • 1970-01-01
  • 1970-01-01
  • 2012-08-16
  • 1970-01-01
  • 2022-11-07
相关资源
最近更新 更多