显示特定时间范围的值

时间:2017-03-01 08:37:54

标签: sql database oracle average self-join

当我在下面选择时运行:

select name, time, value from table1 where name like '%Z' or '%V'

我得到了结果:

enter image description here

我需要做两件事

1 )查询将每小时运行一次,因此如果我们有12.00.00PM范围应该在(11和12> PM之间,如果它是02.00.00AM范围将是(01和02> AM

2 )第二件事是将'%Z'或'%V'结尾的名称的avg(值)显示为一行,但显示为两栏

以下是在下午12点00分运行查询时所需结果的示例:

enter image description here

2 个答案:

答案 0 :(得分:1)

你要求三件事,可以认为是三个步骤。获取时间窗口非常简单,只是稍微复杂一点,因为您的列是时间戳而不是日期。你暗示这将在一小时内运行,但它可能会稍微后续 - 可能是一两秒钟? - 考虑到这一点可能更安全。您可以使用the trunc() function将日期值修改为所需的精度,因此仅查看当前小时您将截断为HH [24]。然后,您可以cast返回时间戳。您可以使用interval arithmetic查找之前的小时:

alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS.FF3';
alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS.FF3 TZR';

select systimestamp,
  trunc(systimestamp, 'HH24') as a,
  cast(trunc(systimestamp, 'HH24') as timestamp) as b,
  cast(trunc(systimestamp, 'HH24') as timestamp) - interval '1' hour as c
from dual;

SYSTIMESTAMP                   A                   B                       C                      
------------------------------ ------------------- ----------------------- -----------------------
2017-03-01 09:25:39.342 +00:00 2017-03-01 09:00:00 2017-03-01 09:00:00.000 2017-03-01 08:00:00.000

alter session命令只是用于控制不同数据类型的显示方式,以进行比较。 (不要依赖实际代码中的NLS设置;使用to_char()将日期时间值的最终格式设置为字符串)。

请注意,截断的结果现在是一个日期(该输出中的A),因此我将其转换回时间戳(B)。您想要的范围基本上是time >= A and time < B。您可以使用sysdate代替systimestamp作为trunc()的输入。

对于使用systimestampsysdate的示例数据,我找不到任何内容,因此我会在其余部分使用假的固定时间,在CTE中生成以进行分离。我从CTE使用now的地方,您将使用systimestamp或sysdate。

第二部分是获取该时间段内每个名字的平均值。这是一个简单的聚合:

with fake_time(now) as (
  select timestamp '2017-02-10 13:01:07' from dual
)
select name,
  avg(value) as avg_value,
  cast(trunc(now, 'HH24') as timestamp) as time
from fake_time
join table1 on time >= cast(trunc(now, 'HH24') as timestamp) - interval '1' hour
and time < cast(trunc(now, 'HH24') as timestamp)
group by name, now;

NAME     AVG_VALUE TIME                   
------- ---------- -----------------------
QWER1_Z         20 2017-02-10 13:00:00.000
QWER1_V         35 2017-02-10 13:00:00.000
TEST1_Z         15 2017-02-10 13:00:00.000
TEST1_V         10 2017-02-10 13:00:00.000

要获取您想要的行,我将假时间设为13:00而不是12:00。您为TEST1_V显示的平均值也是错误的。

下一阶段将它们转换为您想要的格式,作为单行。为此,您可以将根(即TEST1QWER1)和字母(Z或V)添加为结果集中的额外列,然后将其用作{{3}的子查询} - 这需要11克或更高:

with fake_time(now) as (
  select timestamp '2017-02-10 13:01:07' from dual
)
select z_name, z_value, v_name, v_value, time
from (
  select substr(name, 1, length(name) - 2) as root,
    substr(name, -1) as zv,
    name,
    avg(value) as avg_value,
    cast(trunc(now, 'HH24') as timestamp) as time
  from fake_time
  join table1 on time >= cast(trunc(now, 'HH24') as timestamp) - interval '1' hour
  and time < cast(trunc(now, 'HH24') as timestamp)
  group by substr(name, 1, length(name) - 2), name, now
)
pivot (max(name) as name, max(avg_value) as value for (zv) in ('Z' as z, 'V' as v));

Z_NAME     Z_VALUE V_NAME     V_VALUE TIME                   
------- ---------- ------- ---------- -----------------------
TEST1_Z         15 TEST1_V         10 2017-02-10 13:00:00.000
QWER1_Z         20 QWER1_V         35 2017-02-10 13:00:00.000

可能还需要另外一步;在您的示例输出中,您包含了平均值的原始值列表,但没有确认您是否真的想要这些值,或者它们是否只是为了显示平均值是如何计算的,以帮助我们了解您需要做什么。如果您确实希望包含该内容,则可以使用listagg()和连接来构建“平均值”&#39;旋转前的字符串:

    'avg(' || listagg(value, ',') within group (order by value) || ') = ' || avg(value)
      as avg_value,

获取

Z_NAME  Z_VALUE              V_NAME  V_VALUE              TIME                   
------- -------------------- ------- -------------------- -----------------------
TEST1_Z avg(10,20) = 15      TEST1_V avg(10) = 10         2017-02-10 13:00:00.000
QWER1_Z avg(20) = 20         QWER1_V avg(30,40) = 35      2017-02-10 13:00:00.000

正如我之前所说,我只使用fake_date CTE来获取与您的样本数据匹配的日期。您的真实查询更像是:

select z_name, z_value, v_name, v_value, time
from (
  select substr(name, 1, length(name) - 2) as root,
    substr(name, -1) as zv,
    name,
    avg(value) as avg_value,
    cast(trunc(sysdate, 'HH24') as timestamp) as time
  from table1
  where time >= cast(trunc(sysdate, 'HH24') as timestamp) - interval '1' hour
  and time < cast(trunc(sysdate, 'HH24') as timestamp)
  group by substr(name, 1, length(name) - 2), name
)
pivot (max(name) as name, max(avg_value) as value for (zv) in ('Z' as z, 'V' as v));

答案 1 :(得分:0)

select T1.Z, T1.V from (
(select avg(values) from table1 where name like '%Z' and time between sysdate and sysdate - interval '1' group by INSTR(name,'Z')) Z,
(select avg(values) from table1 where name like '%V' and time between sysdate and sysdate - interval '1' group by INSTR(name,'V')) V ) T1
相关问题