Oracle SQL分析查询

时间:2019-02-05 20:37:05

标签: sql oracle analytics

SQL> select LAST_UPDATED_DATE, ODOMETER from demo;

LAST_UPDA   ODOMETER
--------- ----------
05-OCT-18   47174.77
08-OCT-18
12-OCT-18   50246.37
15-OCT-18
19-OCT-18   53743.11
21-OCT-18
22-OCT-18
25-OCT-18   58789.22

8 rows selected.

我需要确定里程表的值(其中它为null),这必须使用SQL来完成。我当时想的方法是-

  1. 获取上一个和下一个里程表的上一个和下一个非空值,以及天数之间的差值,据此可以计算出每天的平均行进距离。

例如在这种情况下(50246.37-47174.77)/(12-OCT-18-05-OCT-18)=〜439

  1. 现在使用每天的平均值,计算天数之间的差,然后将其乘以avg。

例如,(08-OCT-18-05-OCT-18)= 3天,并且连续3天439 * 3 =1317。因此,08年10月18日的值可以是47174.77 + 1317 = 48491.77

现在,我需要为此编写SQL代码的帮助。

任何帮助将不胜感激。

4 个答案:

答案 0 :(得分:2)

您可以使用累积的最大值和最小值来获得上一行和下一行(这假设里程表仅沿一个方向运行)。其余只是算术插值的算术运算:

select d.last_updated_date, d.odometer,
       (case when d.odometer is not null then d.odometer
             else prev_o + (next_o - prev_o) * (last_updated_date - prev_lud) / (next_lud - prev_lud)
        end)
from (select d.*,
             max(case when odometer is not null then last_updated_date end) over (order by last_updated_date) as prev_lud,
             max(odometer) over (order by last_updated_date) as prev_o,
             min(case when odometer is not null then last_updated_date end) over (order by last_updated_date desc) as next_lud,
             min(odometer) over (order by last_updated_date desc) as next_o
      from demo d
     ) d;

答案 1 :(得分:2)

这就是我要做的。这可能有助于解决其他类似问题(线性插值),在这些问题中不能假定“值”随时间增加。对于里程表而言,该假设非常合理,而戈登·利诺夫(Gordon Linoff)的解决方案更简单。我为“数量”可能随时间上升或下降的其他应用程序提供此解决方案。

with
  sample_data(last_updated_date, odometer) as (
    select to_date('05-OCT-18', 'dd-MON-rr'), 47174.77 from dual union all
    select to_date('08-OCT-18', 'dd-MON-rr'), null     from dual union all
    select to_date('12-OCT-18', 'dd-MON-rr'), 50246.37 from dual union all
    select to_date('15-OCT-18', 'dd-MON-rr'), null     from dual union all
    select to_date('19-OCT-18', 'dd-MON-rr'), 53743.11 from dual union all
    select to_date('21-OCT-18', 'dd-MON-rr'), null     from dual union all
    select to_date('22-OCT-18', 'dd-MON-rr'), null     from dual union all
    select to_date('25-OCT-18', 'dd-MON-rr'), 58789.22 from dual
  )
, prep(last_updated_date, odometer, prev_date, next_date, prev_odo, next_odo) as (
    select last_updated_date, odometer,
           case when odometer is null
                then max(nvl2(odometer, last_updated_date, null))
                     over (order by last_updated_date) end,
           case when odometer is null
                then min(nvl2(odometer, last_updated_date, null))
                     over (order by last_updated_date 
                     rows between 1 following and unbounded following) end,
           last_value(odometer ignore nulls) over (order by last_updated_date),
           first_value(odometer ignore nulls) over (order by last_updated_date 
                                rows between 1 following and unbounded following)
    from   sample_data
  )
select   last_updated_date,
         nvl( odometer,
              round(prev_odo + (next_odo - prev_odo) * 
                   (last_updated_date - prev_date) / (next_date - prev_date), 2)
            ) as odometer
from     prep
order by last_updated_date
;

输出

LAST_UPDATED_DATE   ODOMETER
----------------- ----------
05-OCT-18           47174.77
08-OCT-18           48491.17
12-OCT-18           50246.37
15-OCT-18           51744.97
19-OCT-18           53743.11
21-OCT-18           55425.15
22-OCT-18           56266.17
25-OCT-18           58789.22

答案 2 :(得分:1)

这是一个查询,它将为您提供缺少的值。它使用两个常规联接来找到里程表值可用的上一条和下一条记录。

SELECT
    d.last_update_date,
    d0.odometer 
        + (d1.odometer - d0.odometer) * ( d.last_update_date - d0.last_update_date ) 
        / ( d1.last_update_date - d0.last_update_date ) odometer
FROM
    demo d
    INNER JOIN demo d0 ON d0.last_update_date = (
        SELECT MAX(last_update_date) 
        FROM demo 
        WHERE odometer IS NOT NULL AND last_update_date < d.last_update_date
    )
    INNER JOIN demo d1 ON d1.last_update_date = (
        SELECT MIN(last_update_date) 
        FROM demo 
        WHERE odometer IS NOT NULL AND last_update_date > d.last_update_date
    )
WHERE d.odometer IS NULL;

DB Fiddle demo 返回:

 LAST_UPDATE_DATE | ODOMETER
 :--------------- | ----------:
 08-OCT-18        | 48491.17
 15-OCT-18        | 51744.97
 21-OCT-18        | 55425.15
 22-OCT-18        | 56266.17

10月8日的值似乎正是您所期望的值。


如果要实际更新表以添加缺少的值,则可以使用Oracle MERGE语法,如下所示: this db fiddle

MERGE INTO demo target 
USING (
    SELECT
        d.last_update_date,
        d0.odometer 
            + (d1.odometer - d0.odometer) * ( d.last_update_date - d0.last_update_date ) 
            / ( d1.last_update_date - d0.last_update_date ) odometer
    FROM
        demo d
        INNER JOIN demo d0 ON d0.last_update_date = (
            SELECT MAX(last_update_date) 
            FROM demo 
            WHERE odometer IS NOT NULL AND last_update_date < d.last_update_date
        )
        INNER JOIN demo d1 ON d1.last_update_date = (
            SELECT MIN(last_update_date) 
            FROM demo 
            WHERE odometer IS NOT NULL AND last_update_date > d.last_update_date
        )
    WHERE d.odometer IS NULL
) src ON (src.last_update_date = target.last_update_date)
WHEN MATCHED THEN UPDATE SET target.odometer = src.odometer;

答案 3 :(得分:0)

您可以使用OUTER APPLY(从Oracle 12c开始)使用ODOMETER获取上一行和下一行。然后使用公式进行线性插值。

select
  demo.last_updated_date,
  coalesce
  (
    demo.odometer,
    prev.odometer + ( (next.odometer - prev.odometer) *
                      (demo.last_updated_date - prev.last_updated_date) /
                      (next.last_updated_date - prev.last_updated_date) )
  ) as odometer
from demo
outer apply
(
  select *
  from demo d
  where d.last_updated_date < demo.last_updated_date
  and d.odometer is not null
  order by d.last_updated_date desc
  fetch first row only
) prev
outer apply
(
  select *
  from demo d
  where d.last_updated_date > demo.last_updated_date
  and d.odometer is not null
  order by d.last_updated_date desc
  fetch first row only
) next
order by demo.last_updated_date;

结果(四舍五入):

LAST_UPDATED_DATE | ODOMETER
------------------+---------
05-OCT-18         | 47174.77
08-OCT-18         | 48916.94
12-OCT-18         | 50246.37
15-OCT-18         | 52217.80
19-OCT-18         | 53743.11
21-OCT-18         | 55425.15
22-OCT-18         | 56266.17
25-OCT-18         | 58789.22

演示:https://dbfiddle.uk/?rdbms=oracle_18&fiddle=54cc9d4b7dd5793e1c0025627fd929de