2009-10-05 4 views
0

Привет я столкнулся с трудной проблемой: (. Много 100 миллионов записей в размере)Oracle: Найти предыдущий рекорд ранжированный список прогнозов

У меня есть таблица (Oracle 9i) прогнозов погоды чей макияж выглядит следующим образом:

stationid forecastdate forecastinterval forecastcreated forecastvalue 
--------------------------------------------------------------------------------- 
varchar (pk) datetime (pk) integer (pk)  datetime (pk)  integer 

где:

  • stationid относится к одному из многочисленных метеорологических станций, которые могут создать прогноз;
  • forecastdate относится к дате прогноз для
  • forecastinterval относится к часу в forecastdate для прогноза (0 - 23) (дата не только время.).
  • forecastcreated относится ко времени прогноза, может быть много дней заранее.
  • forecastvalue относится к фактическому значению прогноза (как следует из названия.)

нужно определить для данного stationid и заданной forecastdate и forecastinterval пары, в записи, в которых forecastvalue приращения больше номинальное число (скажем, 500). Я покажу таблицу состояния здесь:

stationid forecastdate forecastinterval forecastcreated forecastvalue 
--------------------------------------------------------------------------------- 
'stationa' 13-dec-09  10     10-dec-09 04:50:10 0 
'stationa' 13-dec-09  10     10-dec-09 17:06:13 0 
'stationa' 13-dec-09  10     12-dec-09 05:20:50 300 
'stationa' 13-dec-09  10     13-dec-09 09:20:50 300 

В приведенном выше сценарии я хотел бы снять третью пластинку. Это рекорд, когда значение прогноза увеличилось на номинальную (скажем, 100) сумму.

задача оказывается очень трудно из-за огромного размера таблицы (много 100s миллионов записей.) И так долго, чтобы закончить (так долго, что мой запрос никогда не вернулся.)

Вот моя попытка до сих пор, чтобы захватить эти значения: предложение

select 
    wtr.stationid, 
    wtr.forecastcreated, 
    wtr.forecastvalue, 
    (wtr.forecastdate + wtr.forecastinterval/24) fcst_date 
from 
    (select inner.* 
      rank() over (partition by stationid, 
            (inner.forecastdate + inner.forecastinterval), 
            inner.forecastcreated 
         order by stationid, 
            (inner.forecastdate + inner.forecastinterval) asc, 
            inner.forecastcreated asc 
      ) rk 
     from weathertable inner) wtr 
     where 
     wtr.forecastvalue - 100 > (
        select lastvalue 
         from (select y.*, 
          rank() over (partition by stationid, 
              (forecastdate + forecastinterval), 
              forecastcreated 
             order by stationid, 
              (forecastdate + forecastinterval) asc, 
              forecastcreated asc) rk 
          from weathertable y 
          ) z 
         where z.stationid = wtr.stationid 
          and z.forecastdate = wtr.forecastdate             
          and (z.forecastinterval =  
             wtr.forecastinterval) 
/* here is where i try to get the 'previous' forecast value.*/ 
          and wtr.rk = z.rk + 1) 
+0

Есть ли у вас соответствующие индексы на столе? Если так, то кто они? –

+0

Все поля первичного ключа индексируются. –

ответ

1

Rexem о использовании LAG() это правильный подход, но мы должны использовать пункт секционирования. Это становится ясно, когда мы добавим строки для различных интервалов и разных станций ...

SQL> select * from t 
    2/ 
STATIONID FORECASTDATE INTERVAL FORECASTCREATED  FORECASTVALUE 
---------- ------------ -------- ------------------- ------------- 
stationa 13-12-2009   10 10-12-2009 04:50:10    0 
stationa 13-12-2009   10 10-12-2009 17:06:13    0 
stationa 13-12-2009   10 12-12-2009 05:20:50   300 
stationa 13-12-2009   10 13-12-2009 09:20:50   300 
stationa 13-12-2009   11 13-12-2009 09:20:50   400 
stationb 13-12-2009   11 13-12-2009 09:20:50   500 

6 rows selected. 

SQL> SELECT v.stationid, 
    2   v.forecastcreated, 
    3   v.forecastvalue, 
    4   (v.forecastdate + v.forecastinterval/24) fcst_date 
    5 FROM (SELECT t.stationid, 
    6     t.forecastdate, 
    7     t.forecastinterval, 
    8     t.forecastcreated, 
    9     t.forecastvalue, 
10     t.forecastvalue - LAG(t.forecastvalue, 1) 
11      OVER (ORDER BY t.forecastcreated) as difference 
12   FROM t) v 
13 WHERE v.difference >= 100 
14/ 
STATIONID FORECASTCREATED  FORECASTVALUE FCST_DATE 
---------- ------------------- ------------- ------------------- 
stationa 12-12-2009 05:20:50   300 13-12-2009 10:00:00 
stationa 13-12-2009 09:20:50   400 13-12-2009 11:00:00 
stationb 13-12-2009 09:20:50   500 13-12-2009 11:00:00 

SQL> 

Для удаления ложных срабатываний мы группировать LAG() по STATIONID, FORECASTDATE и FORECASTINTERVAL. Обратите внимание, что следующее зависит от внутреннего запроса, возвращающего NULL из первого вычисления каждого окна раздела.

SQL> SELECT v.stationid, 
    2   v.forecastcreated, 
    3   v.forecastvalue, 
    4   (v.forecastdate + v.forecastinterval/24) fcst_date 
    5 FROM (SELECT t.stationid, 
    6     t.forecastdate, 
    7     t.forecastinterval, 
    8     t.forecastcreated, 
    9     t.forecastvalue, 
10     t.forecastvalue - LAG(t.forecastvalue, 1) 
11      OVER (PARTITION BY t.stationid 
12           , t.forecastdate 
13           , t.forecastinterval 
14       ORDER BY t.forecastcreated) as difference 
15   FROM t) v 
16 WHERE v.difference >= 100 
17/

STATIONID FORECASTCREATED  FORECASTVALUE FCST_DATE 
---------- ------------------- ------------- ------------------- 
stationa 12-12-2009 05:20:50   300 13-12-2009 10:00:00 

SQL> 

Работа с большими объемами данных

Вы описываете ваши таблицы как содержащие многие сотни миллионов строк. Такие огромные столы похожи на черные дыры, у них другая физика. Существуют различные потенциальные подходы, в зависимости от ваших потребностей, временные рамки, финансы, версия базы данных и издание, а также любое другое использование данных вашей системы. Это более чем пятиминутный ответ.

Но вот пятиминутный ответ в любом случае.

Предполагая, что таблица представляет собой живую таблицу, она, по-видимому, заполняется добавлением прогнозов по мере их возникновения, что в основном представляет собой операцию добавления. Это означало бы, что прогнозы для любой данной станции разбросаны по всей таблице. Следовательно, индексы только STATIONID или даже FORECASTDATE имели бы плохой фактор кластеризации.

Исходя из этого предположения, я хотел бы предложить вам сначала создать индекс на (STATIONID, FORCASTDATE, FORECASTINTERVAL, FORECASTCREATED, FORECASTVALUE). Это займет некоторое время (и дисковое пространство) для сборки, но это должно значительно ускорить ваши последующие запросы, потому что у него есть все столбцы, необходимые для удовлетворения запроса с помощью INDEX RANGE SCAN, не касаясь таблицы вообще.

+0

Спасибо за прояснение раздела 'partition by'. Я несколько раз запускал эти решения. Таблицы настолько огромны, что для немедленного ответа я поставил ограничение последних 10 дней в моем аргументе where, чтобы заставить небольшой поиск индекса, например (выберите * из t, где t.forecastdate> sysdate - 10) t Не идеальное решение, я думаю, альтернативой является принудительное полное сканирование таблицы с использованием/* + full (t) */(учитывая, что индексирование занимает слишком много времени даже в течение 10 дней с использованием предыдущего подхода.)? –

+0

@ APC: thx, редко можно использовать LEAD/LAG. –