2017-06-14 5 views
0

Je suis en train de comprendre une meilleure façon d'y parvenir:Lag avec la variable solution offset ou équivalent dans l'oracle

Étant donné un exemple de table

DATE PRC_A  PRC_B 
05/17 10   10 
06/17 X   10 
07/17 10   X 
08/17 10   X 
09/17 X   20 

BUT:s'il y a des données manquantes de PRC_A, mais pas à partir de PRC_B, calculez la différence entre les prix PRC_B's d'un jour donné et le jour précédent de PRC_B. Si PRC_B's le prix du jour précédent est également manquant, continuez à remonter le calendrier jusqu'à ce que vous trouviez une ligne non vide.

Par exemple:

06/17 manque prc_A de sorte que la différence entre les prix PRC_B's serait 0. 09/17 la différence serait , étant donné que les prochaines données antérieures disponibles est le 06/17.

Actuellement, j'utilise la fonction LAG pour calculer la différence. Cependant je suis bloqué quand la rangée précédente est également vide. J'ai mis un compteur, et j'ai pensé faire une boucle while mais il devrait y avoir une condition avant la boucle et ça ne marche pas. Je suis nouveau à sql et je ne suis pas habitué à la syntaxe et aux règles. Voici mon code jusqu'ici. Ce n'est pas correct mais c'est mon début:

DECLARE 
counter INT:=1 
BEGIN 
SELECT .... 

case 

    when prc_A is null and prc_B is not null 
    then (prc_B- LAG(prc_B, 1,0) OVER()) 
    when prc_A is null andprc_B is not null and (LAG(prc_B, 1,0) OVER()) is null then 
     case 
      when LAG(prc_B, 2,0) OVER() is null 
      then counter = counter +1 and 
      (prc_B - LAG(prc_B, counter,0) OVER()) 
      end 
      else 
      0 
end as DIFF, 

FROM ... 
ORDER BY .. 

END; 

Alors, comment pourrais-je atteindre mon objectif?

Répondre

2

Cela peut tout être fait, simplement et efficacement, en langage SQL - pas besoin de code de procédure.

Je n'étais pas sûr du format de sortie que vous désirez, mais vous devriez être capable d'adapter la solution à vos besoins.

De ce que vous avez décrit, vous recherchez la fonction LAST_VALUE(), plutôt que LAG(). LAST_VALUE() vous permet d'ignorer les valeurs NULL. Faites attention à la clause de fenêtrage (ROWS BETWEEN...) - la valeur par défaut est d'inclure la ligne courante, et vous ne voulez pas cela, vous devez donc avoir une clause de fenêtrage explicite.

EDIT: Comme @boneist souligne dans un commentaire ci-dessous, car Oracle 11.2 la fonction LAG() Ajout de l'option IGNORE NULLS - et il a l'avantage que par défaut, il regardera « retour » à des lignes précédentes, en commençant à la le plus récent - donc la clause de fenêtrage ne sera pas nécessaire. Dans Oracle 11.2 ou version ultérieure, au lieu de la fonction LAST_VALUE() illustrée ci-dessous, vous pouvez utiliser LAG(prc_b ignore nulls) over (order by dt).

with 
    test_data (dt, prc_a, prc_b) as (
     select date '2017-05-01', 10, 10 from dual union all 
     select date '2017-06-01', null, 10 from dual union all 
     select date '2017-07-01', 10, null from dual union all 
     select date '2017-08-01', 10, null from dual union all 
     select date '2017-09-01', null, 20 from dual 
    ) 
-- End of test data (not part of the solution). SQL query begins below this line. 
select dt, prc_a, prc_b, 
     case when prc_a is null 
      then prc_b - last_value(prc_b ignore nulls) over (order by dt 
           rows between unbounded preceding and 1 preceding) 
     end as prc_b_diff 
from test_data 
; 

DT    PRC_A  PRC_B PRC_B_DIFF 
---------- ---------- ---------- ---------- 
2017-05-01   10   10   
2017-06-01     10   0 
2017-07-01   10      
2017-08-01   10      
2017-09-01     20   10 
+0

bien expliqué! merci de me présenter cette fonction – ana

+0

@mathguy À partir de 11g, [LAG] (https://docs.oracle.com/cloud/latest/db112/SQLRF/functions082.htm#SQLRF00652)/LEAD ont aussi la fonction ignorer les nulls , afin que vous puissiez simplifier votre solution. – Boneist

+0

@Boneist - Bon point! Je n'ai appris qu'Oracle depuis l'année dernière, donc j'aurais dû le savoir - je lis seulement de la documentation pour 11.2 et 12.1; puisque je n'ai jamais eu besoin de ça, je ne l'ai jamais appris. Je vais modifier le post pour ajouter cette alternative. – mathguy