通过trunc(timestamp)分组时出现错误的结果

时间:2016-04-18 08:38:14

标签: sql oracle date group-by

我有一个输入:

01.03.16 15:48:45.772000000
01.03.16 15:48:49.924000000
01.03.16 21:31:08.320000000
01.03.16 21:56:05.201000000
02.03.16 00:11:10.552000000
02.03.16 00:11:11.652000000
02.03.16 01:31:14.359000000
02.03.16 21:41:11.059000000
03.03.16 00:11:06.850000000
03.03.16 11:05:20.343000000
03.03.16 16:07:07.148000000
04.03.16 18:15:14.460000000
04.03.16 18:55:39.206000000
05.03.16 00:15:21.457000000
05.03.16 00:20:14.908000000
05.03.16 00:50:15.641000000
07.03.16 08:45:19.526000000
07.03.16 21:30:20.562000000
07.03.16 21:45:20.402000000
08.03.16 00:11:12.163000000
08.03.16 14:37:46.607000000
08.03.16 23:16:22.713000000
08.03.16 23:16:22.715000000

现在,我们的想法是按日期分组,并在下午6点30分之前和之后计算行数。 。结果应该是:

date        before  after
01.03.16    2   2   
02.03.16    3   1   
03.03.16    3   0   
04.03.16    0   2
05.03.16    3   0
07.03.16    1   2
08.03.16    2   2

如果我按trunc(mytimestamp)进行分组,并使用caseselect语句中设置mytimestamp,我就无法获得正确的结果。

我的实际查询是:

select trunc(tscreate),
  to_timestamp(trunc( trunc(tscreate)),'dd.MM.yy')
    + interval '17' hour + interval '30' minute as referenz_time,
  sum(case when ( tscreate < to_timestamp(trunc( trunc(tscreate)),'dd.MM.yy')
    + interval '17' hour + interval '30' minute) then 1 else 0 end) as beforeTS,
  sum(case when ( tscreate >= to_timestamp(trunc( trunc(tscreate)),'dd.MM.yy')
    + interval '17' hour + interval '30' minute) then 0 else 1 end) as afterTS
from customer
where app = 'abc'
and tscreate >= to_date('01.03.2016','dd.MM.yyyy')
group by trunc(tscreate) order by trunc(tscreate)

beforTS的值似乎计算正确,但afterTS始终与beforeTS相同,但这是不可能的。

2 个答案:

答案 0 :(得分:1)

您作为评论发布的查询几乎可以正常工作 - 它不会出错但却无法获得您想要的结果。在第二个case表达式中,您已将<更改为>=1,但您已将then 0 else 1更改为then 1 else 0 以及和这两个变化相互抵消 - 两者的逻辑结束相同,并且它们都在17:30之前计算时间。

所以只需修复即可得到你想要的结果:

select trunc(tscreate),
  to_timestamp(trunc( trunc(tscreate)),'dd.MM.yy')
    + interval '17' hour + interval '30' minute as referenz_time,
  sum(case when ( tscreate < to_timestamp(trunc( trunc(tscreate)),'dd.MM.yy')
    + interval '17' hour + interval '30' minute) then 1 else 0 end) as beforeTS,
  sum(case when ( tscreate >= to_timestamp(trunc( trunc(tscreate)),'dd.MM.yy')
    + interval '17' hour + interval '30' minute) then 1 else 0 end) as afterTS
from customer
where app = 'abc'
and tscreate >= to_date('01.03.2016','dd.MM.yyyy')
group by trunc(tscreate)
order by trunc(tscreate);

TRUNC(TSCREATE) REFERENZ_TIME                  BEFORETS    AFTERTS
--------------- ---------------------------- ---------- ----------
01-MAR-16       01-MAR-16 17.30.00.000000000          2          2
02-MAR-16       02-MAR-16 17.30.00.000000000          3          1
03-MAR-16       03-MAR-16 17.30.00.000000000          3          0
04-MAR-16       04-MAR-16 17.30.00.000000000          0          2
05-MAR-16       05-MAR-16 17.30.00.000000000          3          0
07-MAR-16       07-MAR-16 17.30.00.000000000          1          2
08-MAR-16       08-MAR-16 17.30.00.000000000          2          2

我会略微简化它,但其中一些归结为个人品味:

select trunc(tscreate),
  cast(trunc(tscreate)
    + interval '17:30' hour to minute as timestamp) as referenz_time,
  count(case when tscreate < cast(trunc(tscreate)
    + interval '17:30' hour to minute as timestamp) then 1 end) as beforeTS,
  count(case when tscreate >= cast(trunc(tscreate)
    + interval '17:30' hour to minute as timestamp) then 1 end) as afterTS
from customer
where app = 'abc'
and tscreate >= timestamp '2016-03-01 00:00:00'
group by trunc(tscreate)
order by trunc(tscreate);

获得相同的结果。或者,如果您不喜欢间隔,则可以使用日期算术:

select trunc(tscreate),
  trunc(tscreate) + 17.5/24 as referenz_time,
  count(case when tscreate < cast(trunc(tscreate) + 17.5/24 as timestamp) then 1 end) as beforeTS,
  count(case when tscreate >= cast(trunc(tscreate) + 17.5/24 as timestamp) then 1 end) as afterTS
from customer
where app = 'abc'
and tscreate >= timestamp '2016-03-01 00:00:00'
group by trunc(tscreate)
order by trunc(tscreate);

(您甚至可以对提取的时间进行字符串比较,正如David Wallace简要建议的那样,即to_char(tscreate, 'HH24:MI') < '17:30'。但即使这是24小时的'安全',我更愿意坚持原始数据类型。)

我不确定你真的需要referenz_time作为时间戳,无论哪种方式用于显示,你都应该明确地格式化日期和时间戳,而不是依赖于NLS设置。

答案 1 :(得分:0)

您仍然可以在任何地方使用trunc(mytimestamp)

 select to_char(trunc(mytimestamp), 'DD.MM.YYYY') 
 from my_table
 group by trunc(mytimestamp)