我们可以编写group by以在Oracle sql中获得订单的第一条记录吗?还是可以为给定的查询建议解决方案?

时间:2018-07-17 09:35:58

标签: sql oracle join group-by

我有一张桌子,每天显示一个设备的好,坏和其他状态。我想显示每台设备的行,其中包含今天的状态和以前的最佳状态(如果时间段内任何时候良好,则为“良好”,否则为前一天的状态)。我正在使用连接,查询如下。

  SELECT t1.devid,
         t1.status AS Today_status,
         t2.status AS yest_status,
         t2.runtime AS yest_runtime
    FROM devtable t1
         INNER JOIN devtable t2
            ON     t1.devid = t2.devid
               AND t1.RUNTIME = '17-jul-2018'
               AND t2.runtime > '30-jun-2018'
ORDER BY t1.devID, (CASE WHEN t2.status LIKE 'G%' THEN 0 END), t2.runtime;

现在,我无法将其分组为每个设备的单个记录(每个设备获取许多记录)。您能为此提出解决方案吗?

2 个答案:

答案 0 :(得分:1)

使用样本数据和结果更容易解释,但这听起来像是您想要的东西:

select devid, runtime, status, prev_status,
  coalesce(good_status, prev_status) as best_status
from (
  select devid, runtime, status,
    lag(status) over (partition by devid order by runtime) as prev_status,
    max(case when status = 'Good' then status end) over (partition by devid) as good_status
  from (
    select devid, runtime, status
    from devtable
    where runtime > date '2018-06-30'
  )
)
where runtime = date '2018-07-17';

最里面的查询限制日期范围;如果您需要一个上限(例如,您今天的示例中不是今天),则将其作为另一个过滤器。

下一层使用lag()max()分析函数为每个ID查找以前的状态以及任何“良好”状态(通过案例表达式)。

外部查询随后过滤以仅显示目标结束日期,并使用coalesce()显示“良好”(如果存在),或者显示先前的状态。

在CTE中包含一些样本数据的演示:

with devtable (devid, runtime, status) as (
            select 1, date '2018-06-30', 'Good' from dual -- should be ignored
  union all select 1, date '2018-07-01', 'a' from dual
  union all select 1, date '2018-07-16', 'b' from dual
  union all select 1, date '2018-07-17', 'c' from dual
  union all select 2, date '2018-07-01', 'Good' from dual
  union all select 2, date '2018-07-16', 'e' from dual
  union all select 2, date '2018-07-17', 'f' from dual
  union all select 3, date '2018-07-01', 'g' from dual
  union all select 3, date '2018-07-16', 'Good' from dual
  union all select 3, date '2018-07-17', 'i' from dual
  union all select 4, date '2018-07-01', 'j' from dual
  union all select 4, date '2018-07-16', 'k' from dual
  union all select 4, date '2018-07-17', 'Good' from dual
)
select devid, runtime, status, prev_status,
  coalesce(good_status, prev_status) as best_status
from (
  select devid, runtime, status,
    lag(status) over (partition by devid order by runtime) as prev_status,
    max(case when status = 'Good' then status end) over (partition by devid) as good_status
  from (
    select devid, runtime, status
    from devtable
    where runtime > date '2018-06-30'
  )
)
where runtime = date '2018-07-17';

     DEVID RUNTIME    STAT PREV BEST
---------- ---------- ---- ---- ----
         1 2018-07-17 c    b    b   
         2 2018-07-17 f    e    Good
         3 2018-07-17 i    Good Good
         4 2018-07-17 Good k    Good

您可以通过将过滤器移到case表达式中来删除最里面的查询:

select devid, runtime, status, prev_status,
  coalesce(good_status, prev_status) as best_status
from (
  select devid, runtime, status,
    lag(status) over (partition by devid order by runtime) as prev_status,
    max(case when runtime > date '2018-06-30' and status = 'Good' then status end)
      over (partition by devid) as good_status
  from devtable
)
where runtime = date '2018-07-17';

但这可能会做更多的工作,因为它会检查和计算很多您不关心的数据。

答案 1 :(得分:0)

分析函数应该执行您想要的操作。尚不清楚您的结果应该是什么样,但这会收集您需要的信息:

SELECT d.*
FROM (SELECT d.*, 
             LAG(d.status) OVER (PARTITION BY d.devid ORDER BY d.runtime) as prev_status,
             LAG(d.runtime) OVER (PARTITION BY d.devid ORDER BY d.runtime) as prev_runtime,
             ROW_NUMBER() OVER (PARTITION BY d.devid ORDER BY d.runtime) as seqnum,
             SUM(CASE WHEN status = 'GOOD' THEN 1 ELSE 0 END) OVER (PARTITION BY d.devid) as num_good
      FROM devtable d
      WHERE d.runtime = DATE '2018-07-17' AND
            d.runtime > DATE '2018-06-2018'
     ) d
WHERE seqnum = 1;