2009-10-05 5 views
0

Salut, je suis confronté à un problème difficile: (. Beaucoup de 100 de millions d'enregistrements de taille)Oracle: Trouver précédent record pour une liste de classement des prévisions

J'ai une table (oracle 9i) des prévisions météorologiques dont la composition ressemble à ceci:

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

où:

  • stationid fait référence à l'une des nombreuses stations météorologiques qui peuvent créer une prévision;
  • forecastdate fait référence à la date à laquelle la prévision est pour
  • forecastinterval fait référence à l'heure dans le forecastdate pour les prévisions (0-23) (par date pas de temps.).
  • forecastcreated se réfère à l'heure à laquelle la prévision a été faite, peut être plusieurs jours à l'avance.
  • forecastvalue fait référence à la valeur réelle de la prévision (comme son nom l'indique.)

Je dois déterminer pour une donnée stationid et une paire donnée forecastdate et forecastinterval, les enregistrements où un forecastvalue incréments plus d'un nombre nominal (disons 500). Je vais montrer un tableau de la condition ici:

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 

Dans le scénario ci-dessus, je voudrais retirer le troisième enregistrement. C'est le record où la valeur de la prévision a augmenté d'un montant nominal (disons 100).

La tâche se révèle être très difficile en raison de la taille de la table (beaucoup de 100s de millions d'enregistrements.) Et si long à la fin (si longtemps en fait que ma requête n'a jamais retourné.)

Voici ma tentative à ce jour pour saisir ces valeurs:

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

Avez-vous des indices pertinents sur la table? Si c'est vrai, que sont-ils? –

+0

Tous les champs de clé primaire sont indexés. –

Répondre

1

suggestion de Rexem d'utiliser LAG() est la bonne approche, mais nous avons besoin d'utiliser une clause de partitionnement. Cela devient évident une fois que nous ajoutons des lignes pour différents intervalles et stations différentes ...

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> 

Pour enlever les faux positifs que nous regroupons le LAG() par StationID, FORECASTDATE et FORECASTINTERVAL. Notez que ce qui suit repose sur la requête interne retournant NULL du premier calcul de chaque fenêtre de partition.

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> 

Travailler avec de grands volumes de données

Vous décrivez vos tables comme contenant plusieurs centaines de millions de lignes. Ces énormes tables sont comme des trous noirs, ils ont une physique différente. Il existe différentes approches possibles, en fonction de vos besoins, des délais, des finances, de la version et de l'édition de la base de données et de toute autre utilisation des données de votre système. C'est plus qu'une réponse de cinq minutes.

Mais voici la réponse de cinq minutes de toute façon.

En supposant que votre table est la table en direct, il est en cours de remplissage sans doute en ajoutant les prévisions comme ils se produisent, ce qui est essentiellement une opération de appending. Cela signifierait que les prévisions pour une station donnée sont dispersées dans le tableau. Par conséquent, les index sur seulement STATIONID ou même FORECASTDATE auraient un mauvais facteur de clustering.

Dans cette hypothèse, la seule chose que je vous suggère d'essayer d'abord est la construction d'un index sur (STATIONID, FORCASTDATE, FORECASTINTERVAL, FORECASTCREATED, FORECASTVALUE). Cela prendra un certain temps (et l'espace disque) pour construire, mais il devrait accélérer considérablement vos requêtes ultérieures, car il a toutes les colonnes nécessaires pour satisfaire la requête avec un SCAN RANGE INDEX sans toucher à toute la table.

+0

Merci pour éclaircir sur la 'partition by'. J'ai exécuté ces solutions plusieurs fois maintenant. Les tables sont si énormes que pour obtenir une réponse immédiate j'ai placé une restriction des 10 derniers jours dans ma clause where pour forcer une petite recherche d'index par exemple (sélectionnez * from t où t.forecastdate> sysdate - 10) t Pas une solution idéale, je suppose que l'alternative est de forcer une analyse de table complète en utilisant/* + full (t) */(considérant qu'il semble que l'indexage prend trop de temps, même pendant 10 jours). –

+0

@APC: thx, rarement l'occasion d'utiliser LEAD/LAG. –

Questions connexes