2016-09-14 4 views
0

Nous avons une requête SQL Oracle pour identifier les enregistrements où la valeur d'une colonne de table a changé d'un enregistrement à l'autre. colonnes correspondantes sont (ID, some_column, FROM_DATE, TO_DATE) lorsque l'ID est pas unique, et FROM_DATE et TO_DATE déterminer l'intervalle de temps pendant lequel le rang particulier pour cet ID a été efficace, c.-à-Optimiser la requête Oracle SQL auto-jointe avec les fonctions analytiques LAG/LEAD?

(ID1, VAL1, 01/01/2016, 03/01/2016) 
(ID1, VAL2, 04/01/2016, 09/01/2016) 
(ID1, VAL3, 10/01/2016, 19/01/2016) 

etc.

Nous pourrions mettre en œuvre ce qui suit en utilisant le autojointure

SELECT N.ID 
     O.SOME_COLUMN OLD_VALUE, 
     N.SOME_COLUMN NEW_VALUE 
FROM OUR_TABLE N, OUR_TABLE O 
WHERE N.ID = O.ID 
    AND N.FROM_DATE - 1 = O.TO_DATE 
    AND N.SOME_COLUMN <> O.SOME_COLUMN 

cependant, puisque la table contient 100 millions d'enregistrements, il frappe tout à fait la performance. Existe-t-il un moyen plus efficace de le faire? Quelqu'un a fait allusion à des fonctions analytiques (par exemple, LAG), mais nous n'avons pas pu trouver de solution de travail jusqu'à présent. Toutes les idées seraient appréciées

+0

Votre question n'est pas claire. Vos dates ne se chevauchent pas du tout, et on ne sait pas quels résultats vous voulez. –

+0

Les dates ne chevauchent en effet pas, elles représentent des intervalles de temps, au cours desquels la ligne de table particulière appartenant à cet ID était efficace. Comme vous le voyez dans l'exemple, le FROM_DATE d'un intervalle est toujours +1 ajouté au TO_DATE de l'intervalle précédent. Nous avons besoin de résultats, où la valeur de SOME_COLUMN a changé d'un intervalle à l'autre. En fait vérifier votre réponse ci-dessous vous semble avoir tout interprété correctement. – hammerfest

Répondre

2

Oui, vous pouvez utiliser LEAD() pour aller chercher la dernière valeur:

SELECT t.id, 
     t.some_column as OLD_VALUE, 
     LEAD(t.some_column) OVER(PARTITION BY t.id ORDER BY t.from_date) as NEW_VALUE 
FROM YourTable t 

Si vous ne souhaitez que des changements, l'envelopper avec une autre sélection et le filtre OLD_VALUE <> NEW_VALUE

1

Si vous voulez que la ancienne valeur et la nouvelle valeur en une seule rangée, puis utilisez lag():

select t.*, 
     lag(some_column) over (partition by id order by from_date) as prev_val 
from t; 

Si les valeurs ne peuvent pas changer (comme suggérer ed par votre exemple de requête):

select t.* 
from (select t.*, 
      lag(some_column) over (partition by id order by from_date) as prev_val 
     from t 
    ) t 
where prev_val <> some_column; 
1

Je pense que c'est l'approche LAG() dont vous parliez.

SELECT * 
    FROM (
    SELECT ID 
      N.SOME_COLUMN NEW_VALUE, 
      N.FROM_DATE, 
      lag(N.SOME_COLUMN) over (partition by N.ID order by FROM_DATE) OLD_VALUE, 
      lag(N.TO_DATE) over (partition by N.ID order by FROM_DATE) OLD_TO_DATE, 
    FROM OUR_TABLE N 
) T 
WHERE FROM_DATE - 1 = OLD_TO_DATE 
    AND NEW_VALUE<> OLD_VALUE; 
+1

Merci. La première des trois réponses presque identiques, j'ai mis la marque d'acceptation ici. – hammerfest

+0

@hammerfest. . . En fait, c'était la troisième réponse. Selon votre raisonnement, vous devriez accepter la réponse de Sagi. Si vous passez la souris sur le "temps" au dessus du nom (qui dit maintenant "répondu il y a 2 heures"), vous verrez l'heure exacte de la réponse. –

+0

Vous semblez avoir raison, j'ai tout simplement changé les deux réponses après avoir essayé les solutions et retourné à la page des questions. J'ai changé la marque d'acceptation maintenant. @vercelli: Je suis désolé, votre réponse est aussi correcte mais ce n'était en effet pas la première. – hammerfest